Initial commit

This commit is contained in:
nmzik
2026-07-16 10:21:41 +02:00
commit 147188b143
427 changed files with 205923 additions and 0 deletions
+646
View File
@@ -0,0 +1,646 @@
#include "loader/elf.h"
#include "common/assert.h"
#include "common/file.h"
#include "common/logging/log.h"
#include <cstring>
#include <memory>
namespace Loader {
static std::unique_ptr<SelfHeader> LoadSelf(Common::File& f) {
if (f.Remaining() < sizeof(SelfHeader)) {
return nullptr;
}
auto self = std::make_unique<SelfHeader>();
f.Read(self.get(), sizeof(SelfHeader));
return self;
}
static std::unique_ptr<SelfSegment[]> LoadSelfSegments(Common::File& f, uint16_t num) {
auto segs = std::make_unique<SelfSegment[]>(num);
f.Read(segs.get(), sizeof(SelfSegment) * num);
return segs;
}
static std::unique_ptr<Elf64_Ehdr> LoadEhdr64(Common::File& f) {
if (f.Remaining() < sizeof(Elf64_Ehdr)) {
return nullptr;
}
auto ehdr = std::make_unique<Elf64_Ehdr>();
f.Read(ehdr.get(), sizeof(Elf64_Ehdr));
return ehdr;
}
static void SaveEhdr64(Common::File& f, const Elf64_Ehdr* ehdr) {
EXIT_IF(ehdr == nullptr);
uint32_t bytes_written = 0;
f.Write(ehdr, sizeof(Elf64_Ehdr), &bytes_written);
EXIT_IF(bytes_written == 0);
}
static std::unique_ptr<Elf64_Phdr[]> LoadPhdr64(Common::File& f, uint64_t offset, Elf64_Half num) {
auto phdr = std::make_unique<Elf64_Phdr[]>(num);
f.Seek(offset);
f.Read(phdr.get(), sizeof(Elf64_Phdr) * num);
return phdr;
}
static void SavePhdr64(Common::File& f, uint64_t offset, Elf64_Half num, const Elf64_Phdr* phdr) {
EXIT_IF(phdr == nullptr);
uint32_t bytes_written = 0;
f.Seek(offset);
f.Write(phdr, sizeof(Elf64_Phdr) * num, &bytes_written);
EXIT_IF(bytes_written == 0);
}
static std::unique_ptr<Elf64_Shdr[]> LoadShdr64(Common::File& f, uint64_t offset, Elf64_Half num) {
if (num == 0) {
return nullptr;
}
auto shdr = std::make_unique<Elf64_Shdr[]>(num);
f.Seek(offset);
f.Read(shdr.get(), sizeof(Elf64_Shdr) * num);
return shdr;
}
static void SaveShdr64(Common::File& f, uint64_t offset, Elf64_Half num, const Elf64_Shdr* shdr) {
if (num == 0) {
return;
}
EXIT_IF(shdr == nullptr);
uint32_t bytes_written = 0;
f.Seek(offset);
f.Write(shdr, sizeof(Elf64_Shdr) * num, &bytes_written);
EXIT_IF(bytes_written == 0);
}
static std::unique_ptr<uint8_t[]> LoadDynamic64(Elf64* f, uint64_t offset, uint64_t size) {
auto dynamic_data = std::make_unique<uint8_t[]>(size);
// f.Seek(offset);
// f.Read(dynamic_data, size);
f->LoadSegment(reinterpret_cast<uint64_t>(dynamic_data.get()), offset, size);
return dynamic_data;
}
static std::unique_ptr<char[]> LoadStrTable(Common::File& f, uint64_t offset, uint32_t size) {
if (size == 0) {
return nullptr;
}
auto str_table = std::make_unique<char[]>(size);
f.Seek(offset);
f.Read(str_table.get(), size);
return str_table;
}
static void DbgPrintEhdr64(Elf64_Ehdr* ehdr, Common::File& f) {
f.Printf("ehdr->e_ident = ");
for (auto i: ehdr->e_ident) {
f.Printf("%02x", i);
}
f.Printf("\n");
f.Printf("ehdr->e_type = 0x%04" PRIx16 "\n", ehdr->e_type);
f.Printf("ehdr->e_machine = 0x%04" PRIx16 "\n", ehdr->e_machine);
f.Printf("ehdr->e_version = 0x%08" PRIx32 "\n", ehdr->e_version);
f.Printf("ehdr->e_entry = 0x%016" PRIx64 "\n", ehdr->e_entry);
f.Printf("ehdr->e_phoff = 0x%016" PRIx64 "\n", ehdr->e_phoff);
f.Printf("ehdr->e_shoff = 0x%016" PRIx64 "\n", ehdr->e_shoff);
f.Printf("ehdr->e_flags = 0x%08" PRIx32 "\n", ehdr->e_flags);
f.Printf("ehdr->e_ehsize = 0x%04" PRIx16 "\n", ehdr->e_ehsize);
f.Printf("ehdr->e_phentsize = 0x%04" PRIx16 "\n", ehdr->e_phentsize);
f.Printf("ehdr->e_phnum = %" PRIu16 "\n", ehdr->e_phnum);
f.Printf("ehdr->e_shentsize = 0x%04" PRIx16 "\n", ehdr->e_shentsize);
f.Printf("ehdr->e_shnum = %" PRIu16 "\n", ehdr->e_shnum);
f.Printf("ehdr->e_shstrndx = %" PRIu16 "\n", ehdr->e_shstrndx);
}
static void DbgPrintPhdr64(Elf64_Phdr* phdr, Common::File& f) {
f.Printf("phdr->p_type = 0x%08" PRIx32 "\n", phdr->p_type);
f.Printf("phdr->p_flags = 0x%08" PRIx32 "\n", phdr->p_flags);
f.Printf("phdr->p_offset = 0x%016" PRIx64 "\n", phdr->p_offset);
f.Printf("phdr->p_vaddr = 0x%016" PRIx64 "\n", phdr->p_vaddr);
f.Printf("phdr->p_paddr = 0x%016" PRIx64 "\n", phdr->p_paddr);
f.Printf("phdr->p_filesz = 0x%016" PRIx64 "\n", phdr->p_filesz);
f.Printf("phdr->p_memsz = 0x%016" PRIx64 "\n", phdr->p_memsz);
f.Printf("phdr->p_align = 0x%016" PRIx64 "\n", phdr->p_align);
}
static void DbgPrintShdr64(Elf64_Shdr* shdr, Common::File& f) {
f.Printf("shdr->sh_name = %d\n", shdr->sh_name);
f.Printf("shdr->sh_type = 0x%08" PRIx32 "\n", shdr->sh_type);
f.Printf("shdr->sh_flags = 0x%016" PRIx64 "\n", shdr->sh_flags);
f.Printf("shdr->sh_addr = 0x%016" PRIx64 "\n", shdr->sh_addr);
f.Printf("shdr->sh_offset = 0x%016" PRIx64 "\n", shdr->sh_offset);
f.Printf("shdr->sh_size = 0x%016" PRIx64 "\n", shdr->sh_size);
f.Printf("shdr->sh_link = %" PRId32 "\n", shdr->sh_link);
f.Printf("shdr->sh_info = 0x%08" PRIx32 "\n", shdr->sh_info);
f.Printf("shdr->sh_addralign = 0x%016" PRIx64 "\n", shdr->sh_addralign);
f.Printf("shdr->sh_entsize = 0x%016" PRIx64 "\n", shdr->sh_entsize);
}
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define DBG_NAME(tag) \
case tag: name = #tag; break;
static void DbgPrintDynamic64(const Elf64_Dyn* dyn, Common::File& f) {
const char* name = "Unknown";
switch (dyn->d_tag) {
DBG_NAME(DT_OS_HASH)
DBG_NAME(DT_HASH)
DBG_NAME(DT_OS_STRTAB)
DBG_NAME(DT_OS_STRSZ)
DBG_NAME(DT_STRTAB)
DBG_NAME(DT_STRSZ)
DBG_NAME(DT_OS_SYMTAB)
DBG_NAME(DT_SYMTAB)
DBG_NAME(DT_OS_HASHSZ)
DBG_NAME(DT_OS_SYMTABSZ)
DBG_NAME(DT_INIT)
DBG_NAME(DT_FINI)
DBG_NAME(DT_OS_PLTGOT)
DBG_NAME(DT_PLTGOT)
DBG_NAME(DT_OS_JMPREL)
DBG_NAME(DT_JMPREL)
DBG_NAME(DT_OS_PLTRELSZ)
DBG_NAME(DT_PLTRELSZ)
DBG_NAME(DT_OS_PLTREL)
DBG_NAME(DT_PLTREL)
DBG_NAME(DT_OS_RELA)
DBG_NAME(DT_RELA)
DBG_NAME(DT_OS_RELASZ)
DBG_NAME(DT_RELASZ)
DBG_NAME(DT_OS_RELAENT)
DBG_NAME(DT_RELAENT)
DBG_NAME(DT_INIT_ARRAY)
DBG_NAME(DT_INIT_ARRAYSZ)
DBG_NAME(DT_FINI_ARRAY)
DBG_NAME(DT_FINI_ARRAYSZ)
DBG_NAME(DT_PREINIT_ARRAY)
DBG_NAME(DT_PREINIT_ARRAYSZ)
DBG_NAME(DT_OS_SYMENT)
DBG_NAME(DT_SYMENT)
DBG_NAME(DT_DEBUG)
DBG_NAME(DT_TEXTREL)
DBG_NAME(DT_FLAGS)
DBG_NAME(DT_NEEDED)
DBG_NAME(DT_OS_NEEDED_MODULE)
DBG_NAME(DT_OS_NEEDED_MODULE_1)
DBG_NAME(DT_OS_IMPORT_LIB)
DBG_NAME(DT_OS_IMPORT_LIB_1)
DBG_NAME(DT_OS_IMPORT_LIB_ATTR)
DBG_NAME(DT_OS_FINGERPRINT)
DBG_NAME(DT_OS_ORIGINAL_FILENAME)
DBG_NAME(DT_OS_ORIGINAL_FILENAME_1)
DBG_NAME(DT_OS_MODULE_INFO)
DBG_NAME(DT_OS_MODULE_INFO_1)
DBG_NAME(DT_OS_MODULE_ATTR)
DBG_NAME(DT_SONAME)
DBG_NAME(DT_OS_EXPORT_LIB)
DBG_NAME(DT_OS_EXPORT_LIB_1)
DBG_NAME(DT_OS_EXPORT_LIB_ATTR)
DBG_NAME(DT_RELACOUNT)
DBG_NAME(DT_NULL)
}
f.Printf("d_tag = 0x%016" PRIx64 ", d_val = 0x%016" PRIx64 ", name = %s\n", dyn->d_tag,
dyn->d_un.d_val, name);
}
Elf64::~Elf64() {
Clear();
}
void Elf64::LoadSegment(uint64_t vaddr, uint64_t file_offset, uint64_t size) {
EXIT_IF(m_f == nullptr);
if (m_self != nullptr) {
EXIT_IF(m_self_segments == nullptr);
EXIT_IF(m_phdr == nullptr);
for (uint16_t i = 0; i < m_self->segments_num; i++) {
const auto& seg = m_self_segments[i];
if ((seg.type & 0x800u) != 0) {
auto phdr_id = ((seg.type >> 20u) & 0xFFFu);
const auto& phdr = m_phdr[phdr_id];
if (file_offset >= phdr.p_offset && file_offset < phdr.p_offset + phdr.p_filesz) {
EXIT_NOT_IMPLEMENTED(seg.decompressed_size != phdr.p_filesz);
EXIT_NOT_IMPLEMENTED(seg.compressed_size != seg.decompressed_size);
auto offset = file_offset - phdr.p_offset;
EXIT_NOT_IMPLEMENTED(offset + size > seg.decompressed_size);
m_f->Seek(offset + seg.offset);
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), size);
return;
}
}
}
if (m_f->Size() - m_self->file_size == size) {
m_f->Seek(m_self->file_size);
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), size);
return;
}
EXIT("missing self segment\n");
} else {
m_f->Seek(file_offset);
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(vaddr)), size);
}
}
const Elf64_Dyn* Elf64::GetDynValue(Elf64_Sxword tag) const {
for (const auto* dyn = GetDynamic(); dyn->d_tag != DT_NULL; dyn++) {
if (dyn->d_tag == tag) {
return dyn;
}
}
return nullptr;
}
std::vector<const Elf64_Dyn*> Elf64::GetDynList(Elf64_Sxword tag) const {
std::vector<const Elf64_Dyn*> ret;
for (const auto* dyn = GetDynamic(); dyn->d_tag != DT_NULL; dyn++) {
if (dyn->d_tag == tag) {
ret.push_back(dyn);
}
}
return ret;
}
bool Elf64::IsShared() const {
return (m_ehdr->e_type == ET_DYNAMIC);
}
bool Elf64::IsNextGen() const {
return (m_ehdr->e_ident[EI_ABIVERSION] == 2);
}
const char* Elf64::GetSectionName(int index) const {
if (m_ehdr == nullptr || m_shdr == nullptr || m_str_table == nullptr || index < 0 ||
index >= m_ehdr->e_shnum) {
return nullptr;
}
const auto name_offset = m_shdr[index].sh_name;
if (name_offset >= m_str_table_size) {
return nullptr;
}
const auto remaining = m_str_table_size - name_offset;
if (std::memchr(m_str_table.get() + name_offset, '\0', remaining) == nullptr) {
return nullptr;
}
return m_str_table.get() + name_offset;
}
void Elf64::Clear() {
if (m_f != nullptr) {
m_f->Close();
}
m_f.reset();
m_self.reset();
m_ehdr.reset();
m_self_segments.reset();
m_phdr.reset();
m_shdr.reset();
m_str_table.reset();
m_str_table_size = 0;
m_dynamic.reset();
m_dynamic_data.reset();
}
void Elf64::DbgDump(const std::string& folder) {
auto folder_str = Common::FixDirectorySlash(folder);
Common::File::CreateDirectories(folder_str);
for (uint16_t i = 0; i < m_ehdr->e_phnum; i++) {
if (m_phdr[i].p_filesz == 0u) {
continue;
}
char str[512];
int s = snprintf(str, 512, "phdr_%03d", i);
EXIT_NOT_IMPLEMENTED(s >= 512);
Common::File fout;
fout.Create(folder_str + str);
auto buf = std::make_unique<char[]>(static_cast<uint32_t>(m_phdr[i].p_filesz));
// m_f->Seek(m_phdr[i].p_offset);
// m_f->Read(buf, static_cast<uint32_t>(m_phdr[i].p_filesz));
LoadSegment(reinterpret_cast<uint64_t>(buf.get()), m_phdr[i].p_offset, m_phdr[i].p_filesz);
fout.Write(buf.get(), static_cast<uint32_t>(m_phdr[i].p_filesz));
fout.Close();
}
for (uint16_t i = 0; i < m_ehdr->e_shnum; i++) {
if (m_shdr[i].sh_size == 0u) {
continue;
}
char str[512];
int s = snprintf(str, 512, "shdr_%03d", i);
EXIT_NOT_IMPLEMENTED(s >= 512);
Common::File fout;
fout.Create(folder_str + str);
auto buf = std::make_unique<char[]>(static_cast<uint32_t>(m_shdr[i].sh_size));
m_f->Seek(m_shdr[i].sh_offset);
m_f->Read(buf.get(), static_cast<uint32_t>(m_shdr[i].sh_size));
fout.Write(buf.get(), static_cast<uint32_t>(m_shdr[i].sh_size));
fout.Close();
}
Common::File fout;
fout.Create(folder_str + "ehdr.txt");
DbgPrintEhdr64(m_ehdr.get(), fout);
fout.Close();
fout.Create(folder_str + "phdr.txt");
for (uint16_t i = 0; i < m_ehdr->e_phnum; i++) {
fout.Printf("--- phdr [%d] ---\n", i);
DbgPrintPhdr64(m_phdr.get() + i, fout);
}
fout.Close();
fout.Create(folder_str + "shdr.txt");
for (uint16_t i = 0; i < m_ehdr->e_shnum; i++) {
const char* section_name = GetSectionName(i);
fout.Printf("--- shdr [%d] %s ---\n", i, section_name != nullptr ? section_name : "");
DbgPrintShdr64(m_shdr.get() + i, fout);
}
fout.Close();
fout.Create(folder_str + "dynamic.txt");
for (const auto* dyn = GetDynamic(); dyn->d_tag != DT_NULL; dyn++) {
DbgPrintDynamic64(dyn, fout);
}
fout.Close();
}
uint64_t Elf64::GetEntry() {
return m_ehdr->e_entry;
}
bool Elf64::IsSelf() const {
if (m_f == nullptr || m_f->IsInvalid()) {
return false;
}
if (m_self == nullptr) {
return false;
}
const bool known_magic = (m_self->ident[0] == 0x4f && m_self->ident[1] == 0x15 &&
m_self->ident[2] == 0x3d && m_self->ident[3] == 0x1d) ||
(m_self->ident[0] == 0x54 && m_self->ident[1] == 0x14 &&
m_self->ident[2] == 0xf5 && m_self->ident[3] == 0xee);
if (!known_magic) {
return false;
}
const bool known_ident_tail =
(m_self->ident[4] == 0x00 && m_self->ident[5] == 0x01 && m_self->ident[6] == 0x01 &&
m_self->ident[7] == 0x12 && m_self->ident[8] == 0x01 && m_self->ident[9] == 0x01 &&
m_self->ident[10] == 0x00 && m_self->ident[11] == 0x00 && m_self->unknown == 0x22) ||
(m_self->ident[4] == 0x10 && m_self->ident[5] == 0x01 && m_self->ident[6] == 0x01 &&
m_self->ident[7] == 0x12 && m_self->ident[8] == 0x01 && m_self->ident[9] == 0x01 &&
m_self->ident[10] == 0x00 && m_self->ident[11] == 0x10 && m_self->unknown == 0x32);
if (!known_ident_tail) {
LOGF("Unknown SELF file\n");
return false;
}
return true;
}
bool Elf64::IsValid() const {
if (m_f == nullptr || m_f->IsInvalid()) {
return false;
}
if (m_ehdr == nullptr) {
return false;
}
if (m_ehdr->e_ident[EI_MAG0] != '\x7f' || m_ehdr->e_ident[EI_MAG1] != 'E' ||
m_ehdr->e_ident[EI_MAG2] != 'L' || m_ehdr->e_ident[EI_MAG3] != 'F') {
LOGF("Not an ELF file\n");
return false;
}
if (m_ehdr->e_ident[EI_CLASS] != ELFCLASS64) {
LOGF("ehdr->e_ident[EI_CLASS] (0x%x) != ELFCLASS64\n", m_ehdr->e_ident[EI_CLASS]);
return false;
}
if (m_ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
LOGF("ehdr->e_ident[EI_DATA] (0x%x) != ELFDATA2LSB\n", m_ehdr->e_ident[EI_DATA]);
return false;
}
if (m_ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
LOGF("ehdr->e_ident[EI_VERSION] != EV_CURRENT\n");
return false;
}
if (m_ehdr->e_ident[EI_OSABI] != ELFOSABI_FREEBSD) {
LOGF("ehdr->e_ident[EI_OSABI] (0x%x) != ELFOSABI_FREEBSD\n", m_ehdr->e_ident[EI_OSABI]);
return false;
}
if (m_ehdr->e_ident[EI_ABIVERSION] != 0 && m_ehdr->e_ident[EI_ABIVERSION] != 2) {
LOGF("ehdr->e_ident[EI_ABIVERSION] (0x%x) != (0 or 2)\n", m_ehdr->e_ident[EI_ABIVERSION]);
return false;
}
if (m_ehdr->e_type != ET_DYNEXEC && m_ehdr->e_type != ET_DYNAMIC) {
LOGF("ehdr->e_type (%04x) != ET_DYNEXEC && m_ehdr->e_type != ET_DYNAMIC\n", m_ehdr->e_type);
return false;
}
if (m_ehdr->e_machine != EM_X86_64) {
LOGF("ehdr->e_machine (%04x) != EM_X86_64\n", m_ehdr->e_machine);
return false;
}
if (m_ehdr->e_version != EV_CURRENT) {
LOGF("ehdr->e_version != EV_CURRENT\n");
return false;
}
if (m_ehdr->e_phentsize != sizeof(Elf64_Phdr)) {
LOGF("ehdr->e_phentsize != sizeof(Elf64_Phdr)\n");
return false;
}
if (m_ehdr->e_shentsize > 0 && m_ehdr->e_shentsize != sizeof(Elf64_Shdr)) {
LOGF("ehdr->e_shentsize (%d) != sizeof(Elf64_Shdr)\n", m_ehdr->e_shentsize);
return false;
}
return true;
}
void Elf64::Open(const std::filesystem::path& file_name) {
Clear();
m_f = std::make_unique<Common::File>();
m_f->Open(file_name, Common::File::Mode::Read);
if (m_f->IsInvalid()) {
EXIT("Can't open %s\n", Common::PathToString(file_name).c_str());
}
m_self = LoadSelf(*m_f);
if (!IsSelf()) {
m_self.reset();
m_f->Seek(0);
} else {
m_self_segments = LoadSelfSegments(*m_f, m_self->segments_num);
}
auto ehdr_pos = m_f->Tell();
m_ehdr = LoadEhdr64(*m_f);
if (!IsValid()) {
m_ehdr.reset();
}
if (m_ehdr != nullptr /*&& m_self == nullptr*/) {
m_phdr = LoadPhdr64(*m_f, ehdr_pos + m_ehdr->e_phoff, m_ehdr->e_phnum);
if (m_self == nullptr) {
m_shdr = LoadShdr64(*m_f, ehdr_pos + m_ehdr->e_shoff, m_ehdr->e_shnum);
if (m_shdr != nullptr) {
if (m_ehdr->e_shstrndx < m_ehdr->e_shnum) {
m_str_table_size = static_cast<uint32_t>(m_shdr[m_ehdr->e_shstrndx].sh_size);
m_str_table =
LoadStrTable(*m_f, m_shdr[m_ehdr->e_shstrndx].sh_offset, m_str_table_size);
}
}
} else if (m_ehdr->e_shnum != 0) {
LOGF("SELF: skipping ELF section table: shoff=0x%016" PRIx64 ", shnum=%" PRIu16 "\n",
m_ehdr->e_shoff, m_ehdr->e_shnum);
}
for (Elf64_Half i = 0; i < m_ehdr->e_phnum; i++) {
switch (m_phdr[i].p_type) {
case PT_DYNAMIC:
m_dynamic = LoadDynamic64(this, m_phdr[i].p_offset, m_phdr[i].p_filesz);
break;
case PT_OS_DYNLIBDATA:
m_dynamic_data = LoadDynamic64(this, m_phdr[i].p_offset, m_phdr[i].p_filesz);
break;
default: break;
}
}
}
}
void Elf64::Save(const std::filesystem::path& file_name) {
EXIT_IF(!IsValid());
if (IsValid()) {
Common::File f;
f.Create(file_name);
if (f.IsInvalid()) {
EXIT("Can't create %s\n", Common::PathToString(file_name).c_str());
}
SaveEhdr64(f, m_ehdr.get());
SavePhdr64(f, m_ehdr->e_phoff, m_ehdr->e_phnum, m_phdr.get());
SaveShdr64(f, m_ehdr->e_shoff, m_ehdr->e_shnum, m_shdr.get());
for (uint16_t i = 0; i < m_ehdr->e_phnum; i++) {
if (m_phdr[i].p_filesz == 0u) {
continue;
}
auto buf = std::make_unique<char[]>(static_cast<uint32_t>(m_phdr[i].p_filesz));
LoadSegment(reinterpret_cast<uint64_t>(buf.get()), m_phdr[i].p_offset,
m_phdr[i].p_filesz);
uint32_t bytes_written = 0;
f.Seek(m_phdr[i].p_offset);
f.Write(buf.get(), static_cast<uint32_t>(m_phdr[i].p_filesz), &bytes_written);
EXIT_IF(bytes_written == 0);
}
for (uint16_t i = 0; i < m_ehdr->e_shnum; i++) {
if (m_shdr[i].sh_size == 0u) {
continue;
}
auto buf = std::make_unique<char[]>(static_cast<uint32_t>(m_shdr[i].sh_size));
m_f->Seek(m_shdr[i].sh_offset);
m_f->Read(buf.get(), static_cast<uint32_t>(m_shdr[i].sh_size));
uint32_t bytes_written = 0;
f.Seek(m_shdr[i].sh_offset);
f.Write(buf.get(), static_cast<uint32_t>(m_shdr[i].sh_size), &bytes_written);
EXIT_IF(bytes_written == 0);
}
f.Close();
}
}
} // namespace Loader
+308
View File
@@ -0,0 +1,308 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_ELF_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_ELF_H_
#include "common/abi.h"
#include "common/common.h"
#include "common/stringUtils.h"
#include <filesystem>
#include <memory>
#include <vector>
namespace Common {
class File;
} // namespace Common
namespace Loader {
using Elf64_Addr = uint64_t; // Unsigned program address
using Elf64_Off = uint64_t; // Unsigned file offset
using Elf64_Half = uint16_t; // Unsigned medium integer
using Elf64_Word = uint32_t; // Unsigned integer
using Elf64_Sword = int32_t; // Signed integer
using Elf64_Xword = uint64_t; // Unsigned long integer
using Elf64_Sxword = int64_t; // Signed long integer
constexpr int EI_MAG0 = 0;
constexpr int EI_MAG1 = 1;
constexpr int EI_MAG2 = 2;
constexpr int EI_MAG3 = 3;
constexpr int EI_CLASS = 4;
constexpr int EI_DATA = 5;
constexpr int EI_VERSION = 6;
constexpr int EI_OSABI = 7;
constexpr int EI_ABIVERSION = 8;
constexpr int EI_PAD = 9;
constexpr int EI_NIDENT = 16;
constexpr char ELFCLASS64 = 2; // 64-bit objects
constexpr char ELFDATA2LSB = 1; // Object file data structures are little-endian
constexpr Elf64_Half EM_X86_64 = 62; /* AMD x86-64 architecture */
constexpr int EV_CURRENT = 1;
constexpr char ELFOSABI_FREEBSD = 9; // FreeBSD operating system
constexpr Elf64_Half ET_DYNEXEC = 0xfe10; // Executable file
constexpr Elf64_Half ET_DYNAMIC = 0xfe18; // Shared
// #define SHT_PROGBITS 1
constexpr Elf64_Word PT_LOAD = 1;
constexpr Elf64_Word PT_DYNAMIC = 2;
constexpr Elf64_Word PT_TLS = 7;
constexpr Elf64_Word PT_OS_DYNLIBDATA = 0x61000000;
constexpr Elf64_Word PT_OS_PROCPARAM = 0x61000001;
constexpr Elf64_Word PT_OS_RELRO = 0x61000010;
constexpr Elf64_Word PF_X = 0x1; // Execute
constexpr Elf64_Word PF_W = 0x2; // Write
constexpr Elf64_Word PF_R = 0x4; // Read
constexpr Elf64_Sxword DT_DEBUG = 0x00000015;
constexpr Elf64_Sxword DT_FINI = 0x0000000d;
constexpr Elf64_Sxword DT_FINI_ARRAY = 0x0000001a;
constexpr Elf64_Sxword DT_FINI_ARRAYSZ = 0x0000001c;
constexpr Elf64_Sxword DT_FLAGS = 0x0000001e;
constexpr Elf64_Sxword DT_INIT = 0x0000000c;
constexpr Elf64_Sxword DT_INIT_ARRAY = 0x00000019;
constexpr Elf64_Sxword DT_INIT_ARRAYSZ = 0x0000001b;
constexpr Elf64_Sxword DT_NEEDED = 0x00000001;
constexpr Elf64_Sxword DT_OS_EXPORT_LIB = 0x61000013;
constexpr Elf64_Sxword DT_OS_EXPORT_LIB_1 = 0x61000047;
constexpr Elf64_Sxword DT_OS_EXPORT_LIB_ATTR = 0x61000017;
constexpr Elf64_Sxword DT_OS_FINGERPRINT = 0x61000007;
constexpr Elf64_Sxword DT_OS_HASH = 0x61000025;
constexpr Elf64_Sxword DT_OS_HASHSZ = 0x6100003d;
constexpr Elf64_Sxword DT_OS_IMPORT_LIB = 0x61000015;
constexpr Elf64_Sxword DT_OS_IMPORT_LIB_1 = 0x61000049;
constexpr Elf64_Sxword DT_OS_IMPORT_LIB_ATTR = 0x61000019;
constexpr Elf64_Sxword DT_OS_JMPREL = 0x61000029;
constexpr Elf64_Sxword DT_OS_MODULE_ATTR = 0x61000011;
constexpr Elf64_Sxword DT_OS_MODULE_INFO = 0x6100000d;
constexpr Elf64_Sxword DT_OS_MODULE_INFO_1 = 0x61000043;
constexpr Elf64_Sxword DT_OS_NEEDED_MODULE = 0x6100000f;
constexpr Elf64_Sxword DT_OS_NEEDED_MODULE_1 = 0x61000045;
constexpr Elf64_Sxword DT_OS_ORIGINAL_FILENAME = 0x61000009;
constexpr Elf64_Sxword DT_OS_ORIGINAL_FILENAME_1 = 0x61000041;
constexpr Elf64_Sxword DT_OS_PLTGOT = 0x61000027;
constexpr Elf64_Sxword DT_OS_PLTREL = 0x6100002b;
constexpr Elf64_Sxword DT_OS_PLTRELSZ = 0x6100002d;
constexpr Elf64_Sxword DT_OS_RELA = 0x6100002f;
constexpr Elf64_Sxword DT_OS_RELAENT = 0x61000033;
constexpr Elf64_Sxword DT_OS_RELASZ = 0x61000031;
constexpr Elf64_Sxword DT_OS_STRSZ = 0x61000037;
constexpr Elf64_Sxword DT_OS_STRTAB = 0x61000035;
constexpr Elf64_Sxword DT_OS_SYMENT = 0x6100003b;
constexpr Elf64_Sxword DT_OS_SYMTAB = 0x61000039;
constexpr Elf64_Sxword DT_OS_SYMTABSZ = 0x6100003f;
constexpr Elf64_Sxword DT_PREINIT_ARRAY = 0x00000020;
constexpr Elf64_Sxword DT_PREINIT_ARRAYSZ = 0x00000021;
constexpr Elf64_Sxword DT_REL = 0x00000011;
constexpr Elf64_Sxword DT_RELA = 0x00000007;
constexpr Elf64_Sxword DT_SONAME = 0x0000000e;
constexpr Elf64_Sxword DT_TEXTREL = 0x00000016;
constexpr Elf64_Sxword DT_HASH = 0x00000004;
constexpr Elf64_Sxword DT_STRTAB = 0x00000005;
constexpr Elf64_Sxword DT_STRSZ = 0x0000000a;
constexpr Elf64_Sxword DT_SYMTAB = 0x00000006;
constexpr Elf64_Sxword DT_SYMENT = 0x0000000b;
constexpr Elf64_Sxword DT_PLTGOT = 0x00000003;
constexpr Elf64_Sxword DT_PLTREL = 0x00000014;
constexpr Elf64_Sxword DT_JMPREL = 0x00000017;
constexpr Elf64_Sxword DT_PLTRELSZ = 0x00000002;
constexpr Elf64_Sxword DT_RELASZ = 0x00000008;
constexpr Elf64_Sxword DT_RELAENT = 0x00000009;
constexpr Elf64_Sxword DT_RELACOUNT = 0x6ffffff9;
constexpr Elf64_Sxword DT_NULL = 0;
constexpr Elf64_Word R_X86_64_64 = 1;
constexpr Elf64_Word R_X86_64_GLOB_DAT = 6;
constexpr Elf64_Word R_X86_64_JUMP_SLOT = 7;
constexpr Elf64_Word R_X86_64_RELATIVE = 8;
constexpr Elf64_Word R_X86_64_DTPMOD64 = 16;
constexpr uint8_t STB_LOCAL = 0;
constexpr uint8_t STB_GLOBAL = 1;
constexpr uint8_t STB_WEAK = 2;
constexpr uint8_t STT_NOTYPE = 0;
constexpr uint8_t STT_OBJECT = 1;
constexpr uint8_t STT_FUNC = 2;
#pragma pack(1)
struct SelfHeader {
uint8_t ident[12];
uint16_t size1;
uint16_t size2;
uint64_t file_size;
uint16_t segments_num;
uint16_t unknown;
uint32_t pad;
};
struct SelfSegment {
uint64_t type;
uint64_t offset;
uint64_t compressed_size;
uint64_t decompressed_size;
};
struct Elf64_Ehdr // NOLINT(readability-identifier-naming)
{
uint8_t e_ident[EI_NIDENT]; /* ELF identification */
Elf64_Half e_type; /* Object file type */
Elf64_Half e_machine; /* Machine type */
Elf64_Word e_version; /* Object file version */
Elf64_Addr e_entry; /* Entry point address */
Elf64_Off e_phoff; /* Program header offset */
Elf64_Off e_shoff; /* Section header offset */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size */
Elf64_Half e_phentsize; /* Size of program header entry */
Elf64_Half e_phnum; /* Number of program header entries */
Elf64_Half e_shentsize; /* Size of section header entry */
Elf64_Half e_shnum; /* Number of section header entries */
Elf64_Half e_shstrndx; /* Section name string table index */
};
struct Elf64_Phdr // NOLINT(readability-identifier-naming)
{
Elf64_Word p_type; /* Type of segment */
Elf64_Word p_flags; /* Segment attributes */
Elf64_Off p_offset; /* Offset in file */
Elf64_Addr p_vaddr; /* Virtual address in memory */
Elf64_Addr p_paddr; /* Reserved */
Elf64_Xword p_filesz; /* Size of segment in file */
Elf64_Xword p_memsz; /* Size of segment in memory */
Elf64_Xword p_align; /* Alignment of segment */
};
struct Elf64_Shdr // NOLINT(readability-identifier-naming)
{
Elf64_Word sh_name; /* Section name */
Elf64_Word sh_type; /* Section type */
Elf64_Xword sh_flags; /* Section attributes */
Elf64_Addr sh_addr; /* Virtual address in memory */
Elf64_Off sh_offset; /* Offset in file */
Elf64_Xword sh_size; /* Size of section */
Elf64_Word sh_link; /* Link to other section */
Elf64_Word sh_info; /* Miscellaneous information */
Elf64_Xword sh_addralign; /* Address alignment boundary */
Elf64_Xword sh_entsize; /* Size of entries, if section has table */
};
struct Elf64_Dyn // NOLINT(readability-identifier-naming)
{
Elf64_Sxword d_tag;
union {
Elf64_Xword d_val;
Elf64_Addr d_ptr;
} d_un;
};
struct Elf64_Sym // NOLINT(readability-identifier-naming)
{
[[nodiscard]] unsigned char GetBind() const { return st_info >> 4u; }
[[nodiscard]] unsigned char GetType() const { return st_info & 0xfu; }
Elf64_Word st_name;
unsigned char st_info;
unsigned char st_other;
Elf64_Half st_shndx;
Elf64_Addr st_value;
Elf64_Xword st_size;
};
// struct Elf64_Rel // NOLINT(readability-identifier-naming)
//{
// Elf64_Addr r_offset;
// Elf64_Xword r_info;
//};
struct Elf64_Rela // NOLINT(readability-identifier-naming)
{
[[nodiscard]] Elf64_Word GetSymbol() const { return static_cast<Elf64_Word>(r_info >> 32u); }
[[nodiscard]] Elf64_Word GetType() const {
return static_cast<Elf64_Word>(r_info & 0xffffffff);
}
Elf64_Addr r_offset;
Elf64_Xword r_info;
Elf64_Sxword r_addend;
};
struct tls_info_t // NOLINT(readability-identifier-naming)
{
uint64_t addr;
uint64_t filesz;
uint64_t memsz;
};
#pragma pack()
class Elf64 {
public:
Elf64() = default;
virtual ~Elf64();
void Open(const std::filesystem::path& file_name);
void Save(const std::filesystem::path& file_name);
void DbgDump(const std::string& folder);
const char* GetSectionName(int index) const;
[[nodiscard]] bool IsSelf() const;
[[nodiscard]] bool IsValid() const;
[[nodiscard]] bool IsShared() const;
[[nodiscard]] bool IsNextGen() const;
void LoadSegment(uint64_t vaddr, uint64_t file_offset, uint64_t size);
uint64_t GetEntry();
[[nodiscard]] const Elf64_Dyn* GetDynValue(Elf64_Sxword tag) const;
[[nodiscard]] std::vector<const Elf64_Dyn*> GetDynList(Elf64_Sxword tag) const;
[[nodiscard]] bool HasDynValue(Elf64_Sxword tag) const { return GetDynValue(tag) != nullptr; }
KYTY_CLASS_NO_COPY(Elf64);
[[nodiscard]] const Elf64_Dyn* GetDynamic() const {
return reinterpret_cast<const Elf64_Dyn*>(m_dynamic.get());
}
[[nodiscard]] const Elf64_Ehdr* GetEhdr() const { return m_ehdr.get(); }
[[nodiscard]] const Elf64_Phdr* GetPhdr() const { return m_phdr.get(); }
[[nodiscard]] const Elf64_Shdr* GetShdr() const { return m_shdr.get(); }
[[nodiscard]] char* GetStrTable() const { return m_str_table.get(); }
template <class T>
[[nodiscard]] T GetDynamicData(uint64_t offset) const {
return (m_dynamic_data == nullptr ? nullptr
: reinterpret_cast<T>(m_dynamic_data.get() + offset));
}
private:
void Clear();
std::unique_ptr<Common::File> m_f;
std::unique_ptr<SelfHeader> m_self;
std::unique_ptr<SelfSegment[]> m_self_segments;
std::unique_ptr<Elf64_Ehdr> m_ehdr;
std::unique_ptr<Elf64_Phdr[]> m_phdr;
std::unique_ptr<Elf64_Shdr[]> m_shdr;
std::unique_ptr<uint8_t[]> m_dynamic;
std::unique_ptr<uint8_t[]> m_dynamic_data;
std::unique_ptr<char[]> m_str_table;
uint32_t m_str_table_size = 0;
// uint64_t m_base_vaddr = 0;
};
} // namespace Loader
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_ELF_H_ */
+167
View File
@@ -0,0 +1,167 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_JIT_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_JIT_H_
#include "common/abi.h"
namespace Loader::Jit {
#pragma pack(1)
struct JmpWithIndex {
void SetIndex(uint32_t index) { *reinterpret_cast<uint32_t*>(&code[1]) = index; }
void SetFunc(void* handler) {
auto func_addr = reinterpret_cast<int64_t>(handler);
auto rip_addr = reinterpret_cast<int64_t>(&code[10]);
auto offset64 = func_addr - rip_addr;
auto offset32 = static_cast<uint32_t>(static_cast<uint64_t>(offset64) & 0xffffffffu);
*reinterpret_cast<uint32_t*>(&code[6]) = offset32;
}
static uint64_t GetSize() { return 16; }
// 68 00 00 00 00 push <index>
// E9 E0 FF FF FF jmp <handler>
uint8_t code[16] = {0x68, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00,
0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
};
struct CallPlt {
explicit CallPlt(uint32_t table_size) {
for (uint32_t index = 0; index < table_size; index++) {
auto* c = new (&code[32] + JmpWithIndex::GetSize() * index) JmpWithIndex;
c->SetIndex(index);
c->SetFunc(this);
}
}
void SetPltGot(uint64_t vaddr) { *reinterpret_cast<uint64_t*>(&code[2]) = vaddr; }
uint64_t GetAddr(uint32_t index) {
return reinterpret_cast<uint64_t>(&code[32] + JmpWithIndex::GetSize() * index);
}
static uint64_t GetSize(uint32_t table_size) {
return 32 + JmpWithIndex::GetSize() * table_size;
}
// 0: 49 bb 88 77 66 55 44 movabs r11,0x1122334455667788
// 7: 33 22 11
// a: 41 ff 73 08 push QWORD PTR [r11+0x8]
// e: 41 ff 63 10 jmp QWORD PTR [r11+0x10]
uint8_t code[32] = {0x49, 0xBB, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x41, 0xFF,
0x73, 0x08, 0x41, 0xFF, 0x63, 0x10, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
};
struct JmpRax {
template <class Handler>
void SetFunc(Handler func) {
*reinterpret_cast<Handler*>(&code[2]) = func;
}
// mov rax, 0x1122334455667788
// jmp rax
uint8_t code[16] = {0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xFF, 0xE0};
};
struct Call9 {
template <class Handler>
void SetFunc(Handler func) {
auto func_addr = reinterpret_cast<int64_t>(reinterpret_cast<void*>(func));
auto rip_addr = reinterpret_cast<int64_t>(&code[5]);
auto offset64 = func_addr - rip_addr;
auto offset32 = static_cast<uint32_t>(static_cast<uint64_t>(offset64) & 0xffffffffu);
*reinterpret_cast<uint32_t*>(&code[1]) = offset32;
}
void SetOutputReg(uint8_t reg) { code[7] = 0xc0u | (reg & 7u); }
static uint64_t GetSize() { return 9; }
// call func
// mov rax,rax
// nop
uint8_t code[9] = {0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xC0, 0x90};
};
struct TlsRegStub {
template <class Handler>
void SetFunc(Handler func) {
auto func_addr = reinterpret_cast<int64_t>(reinterpret_cast<void*>(func));
auto rip_addr = reinterpret_cast<int64_t>(&code[13]);
auto offset64 = func_addr - rip_addr;
auto offset32 = static_cast<uint32_t>(static_cast<uint64_t>(offset64) & 0xffffffffu);
*reinterpret_cast<uint32_t*>(&code[9]) = offset32;
}
void SetOutputReg(uint8_t reg) { code[15] = 0xc0u | (reg & 7u); }
static uint64_t GetOffset(uint8_t reg) {
return 0x100 + static_cast<uint64_t>(reg) * GetSize();
}
static uint64_t GetSize() { return 32; }
// sub rsp,0x80
// push rax
// call safe_call
// mov <reg>,rax
// pop rax
// add rsp,0x80
// ret
uint8_t code[32] = {0x48, 0x81, 0xEC, 0x80, 0x00, 0x00, 0x00, 0x50, 0xE8, 0x00, 0x00,
0x00, 0x00, 0x48, 0x89, 0xC0, 0x58, 0x48, 0x81, 0xC4, 0x80, 0x00,
0x00, 0x00, 0xC3, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
};
struct SafeCall {
using func_t = KYTY_MS_ABI uint8_t* (*)();
void SetFunc(func_t func) { *reinterpret_cast<func_t*>(&code[0x22]) = func; }
static uint64_t GetSize() { return 0x1000; }
uint8_t code[0x90] = {
/*00*/ 0x48, 0x81, 0xec, 0x80, 0x00, 0x00, 0x00, // sub rsp,0x80
/*07*/ 0x9c, // pushfq
/*08*/ 0x51, // push rcx
/*09*/ 0x52, // push rdx
/*0a*/ 0x41, 0x50, // push r8
/*0c*/ 0x41, 0x51, // push r9
/*0e*/ 0x41, 0x52, // push r10
/*10*/ 0x41, 0x53, // push r11
/*12*/ 0x53, // push rbx
/*13*/ 0x57, // push rdi
/*14*/ 0x56, // push rsi
/*15*/ 0x48, 0x89, 0xe3, // mov rbx,rsp
/*18*/ 0x48, 0x83, 0xe4, 0xf0, // and rsp,0xfffffffffffffff0
/*1c*/ 0x48, 0x83, 0xec, 0x20, // sub rsp,0x20
/*20*/ 0x48, 0xb9, 0x88, 0x77, 0x66, 0x55, 0x44,
0x33, 0x22, 0x11, // movabs rcx,0x1122334455667788
/*2a*/ 0xff, 0xd1, // call rcx
/*2c*/ 0x48, 0x89, 0xc1, // mov rcx,rax
/*2f*/ 0x48, 0x89, 0xdc, // mov rsp,rbx
/*32*/ 0x48, 0x89, 0xc8, // mov rax,rcx
/*35*/ 0x5e, // pop rsi
/*36*/ 0x5f, // pop rdi
/*37*/ 0x5b, // pop rbx
/*38*/ 0x41, 0x5b, // pop r11
/*3a*/ 0x41, 0x5a, // pop r10
/*3c*/ 0x41, 0x59, // pop r9
/*3e*/ 0x41, 0x58, // pop r8
/*40*/ 0x5a, // pop rdx
/*41*/ 0x59, // pop rcx
/*42*/ 0x9d, // popfq
/*43*/ 0x48, 0x81, 0xc4, 0x80, 0x00, 0x00,
0x00, // add rsp,0x80
/*4a*/ 0xc3, // ret
};
};
#pragma pack()
} // namespace Loader::Jit
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_JIT_H_ */
File diff suppressed because it is too large Load Diff
+209
View File
@@ -0,0 +1,209 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_RUNTIMELINKER_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_RUNTIMELINKER_H_
#include "common/abi.h"
#include "common/common.h"
#include "common/stringUtils.h"
#include "common/threads.h"
#include "loader/symbolDatabase.h"
#include <filesystem>
#include <memory>
#include <unordered_map>
#include <vector>
namespace Loader {
class Elf64;
struct Elf64_Sym;
struct Elf64_Rela;
class RuntimeLinker;
using module_func_t = KYTY_SYSV_ABI int (*)(size_t args, const void* argp);
using application_heap_free_func_t = KYTY_SYSV_ABI void (*)(void*);
using application_heap_malloc_func_t = KYTY_SYSV_ABI void* (*)(uint64_t);
using application_heap_posix_memalign_func_t = KYTY_SYSV_ABI int (*)(void**, uint64_t, uint64_t);
struct ModuleId {
bool operator==(const ModuleId& other) const {
return version_major == other.version_major && version_minor == other.version_minor &&
name == other.name;
}
std::string id;
int version_major;
int version_minor;
std::string name;
};
struct LibraryId {
bool operator==(const LibraryId& other) const {
return version == other.version && name == other.name;
}
std::string id;
int version;
std::string name;
};
struct ThreadLocalStorage {
struct Block {
uint8_t* ptr = nullptr;
application_heap_free_func_t free_func = nullptr;
bool vm_alloc = false;
};
~ThreadLocalStorage();
uint64_t image_vaddr = 0;
uint64_t init_size = 0;
uint64_t image_size = 0;
uint64_t tcb_offset = 0;
uint64_t handler_vaddr = 0;
std::vector<uint8_t> init_image;
std::unordered_map<int, Block> tlss;
Common::Mutex mutex;
};
struct DynamicInfo {
void* hash_table = nullptr;
uint64_t hash_table_size = 0;
char* str_table = nullptr;
uint64_t str_table_size = 0;
Elf64_Sym* symbol_table = nullptr;
uint64_t symbol_table_total_size = 0;
uint64_t symbol_table_entry_size = 0;
uint64_t init_vaddr = 0;
uint64_t fini_vaddr = 0;
uint64_t init_array_vaddr = 0;
uint64_t fini_array_vaddr = 0;
uint64_t preinit_array_vaddr = 0;
uint64_t init_array_size = 0;
uint64_t fini_array_size = 0;
uint64_t preinit_array_size = 0;
uint64_t pltgot_vaddr = 0;
Elf64_Rela* jmprela_table = nullptr;
uint64_t jmprela_table_size = 0;
Elf64_Rela* rela_table = nullptr;
uint64_t rela_table_total_size = 0;
uint64_t rela_table_entry_size = 0;
uint64_t relative_count = 0;
uint64_t debug = 0;
uint64_t textrel = 0;
uint64_t flags = 0;
const char* so_name = nullptr;
std::vector<const char*> needed;
std::vector<ModuleId> export_modules;
std::vector<ModuleId> import_modules;
std::vector<LibraryId> export_libs;
std::vector<LibraryId> import_libs;
};
struct Program {
Program();
~Program();
int32_t unique_id = -1;
RuntimeLinker* rt = nullptr;
std::filesystem::path file_name;
std::unique_ptr<Elf64> elf;
std::unique_ptr<DynamicInfo> dynamic_info;
uint64_t base_vaddr = 0;
uint64_t base_size = 0;
uint64_t base_size_aligned = 0;
uint64_t mapped_size = 0;
std::unique_ptr<SymbolDatabase> export_symbols;
std::unique_ptr<SymbolDatabase> import_symbols;
ThreadLocalStorage tls;
bool fail_if_global_not_resolved = true;
bool dbg_print_reloc = false;
bool relocated = false;
uint64_t proc_param_vaddr = 0;
uint64_t custom_call_plt_vaddr = 0;
uint32_t custom_call_plt_num = 0;
};
class RuntimeLinker {
public:
RuntimeLinker();
virtual ~RuntimeLinker();
void Clear();
KYTY_CLASS_NO_COPY(RuntimeLinker);
void DbgDump(const std::string& folder);
Program* LoadProgram(const std::filesystem::path& elf_name);
void SaveMainProgram(const std::filesystem::path& elf_name);
void SaveProgram(Program* program, const std::filesystem::path& elf_name);
void UnloadProgram(Program* program);
[[nodiscard]] uint64_t GetEntry();
[[nodiscard]] uint64_t GetProcParam();
void RelocateAll();
void RelocateProgram(Program* program);
void Execute();
int StartModule(Program* program, size_t args, const void* argp, module_func_t func);
int StopModule(Program* program, size_t args, const void* argp, module_func_t func);
void StartAllModules();
void StopAllModules();
void DeleteTlss(int thread_id);
void SetApplicationHeapApi(void* const api[10]);
void* ApplicationHeapMalloc(uint64_t size);
void* ApplicationHeapMemalign(uint64_t alignment, uint64_t size);
void Resolve(const std::string& name, SymbolType type, Program* program, SymbolRecord* out_info,
bool* bind_self);
bool ResolveLoadedSymbolByNid(const std::string& nid, SymbolType type, SymbolRecord* out_info);
SymbolDatabase* Symbols() { return m_symbols.get(); }
static uint64_t ReadFromElf(Program* program, uint64_t vaddr);
Program* FindProgramByAddr(uint64_t vaddr);
Program* FindProgramById(int32_t id);
Program* FindProgramByFileName(const std::filesystem::path& elf_name);
static uint8_t* TlsGetAddr(Program* program);
static void DeleteTls(Program* program, int thread_id);
void StackTrace(uint64_t frame_ptr);
private:
static void LoadProgramToMemory(Program* program);
static void ParseProgramDynamicInfo(Program* program);
static void CreateSymbolDatabase(Program* program);
static void Relocate(Program* program);
static void DeleteProgram(Program* program);
static void SetupTlsHandler(Program* program);
void PreloadAdjacentPrograms();
Program* FindProgram(const ModuleId& m, const LibraryId& l);
static const ModuleId* FindModule(const Program& program, const std::string& id);
static const LibraryId* FindLibrary(const Program& program, const std::string& id);
std::vector<Program*> m_programs;
std::unique_ptr<SymbolDatabase> m_symbols;
bool m_relocated = false;
Common::Mutex m_mutex;
application_heap_malloc_func_t m_application_heap_malloc = nullptr;
application_heap_free_func_t m_application_heap_free = nullptr;
application_heap_posix_memalign_func_t m_application_heap_posix_memalign = nullptr;
};
} // namespace Loader
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_RUNTIMELINKER_H_ */
+94
View File
@@ -0,0 +1,94 @@
#include "loader/symbolDatabase.h"
#include "common/file.h"
#include "common/magicEnum.h"
#include <fmt/format.h>
namespace Loader {
constexpr char LIB_PREFIX[] = "libSce";
static std::string UpdateName(const std::string& str) {
return Common::StartsWith(str, LIB_PREFIX) ? Common::RemoveFirst(str, 6) : str;
}
std::string SymbolDatabase::GenerateName(const SymbolResolve& s) {
auto library = UpdateName(s.library);
auto module = UpdateName(s.module);
return fmt::format("{}[{}_v{}][{}_v{}.{}][{}]", s.name.c_str(), library.c_str(),
s.library_version, module.c_str(), s.module_version_major,
s.module_version_minor, Common::EnumName(s.type).c_str());
}
void SymbolDatabase::Add(const SymbolResolve& s, uint64_t vaddr) {
SymbolRecord r {};
r.name = GenerateName(s);
r.vaddr = vaddr;
m_map.insert_or_assign(r.name, m_symbols.size());
m_symbols.push_back(r);
}
void SymbolDatabase::Add(const SymbolResolve& s, uint64_t vaddr, const std::string& dbg_name) {
SymbolRecord r {};
r.name = GenerateName(s);
r.vaddr = vaddr;
r.dbg_name = dbg_name;
m_map.insert_or_assign(r.name, m_symbols.size());
m_symbols.push_back(r);
}
void SymbolDatabase::DbgDump(const std::string& folder, const std::string& file_name) {
auto folder_str = Common::FixDirectorySlash(folder);
Common::File::CreateDirectories(folder_str);
Common::File f;
f.Create(folder_str + file_name);
for (const auto& sym: m_symbols) {
f.Printf("%" PRIx64 " %s\n", sym.vaddr, sym.name.c_str());
}
f.Close();
}
const SymbolRecord* SymbolDatabase::Find(const SymbolResolve& s) const {
auto it = m_map.find(GenerateName(s));
if (it == m_map.end()) {
return nullptr;
}
auto index = it->second;
if (index >= m_symbols.size()) {
return nullptr;
}
return &m_symbols[index];
}
const SymbolRecord* SymbolDatabase::FindByNid(const std::string& nid, SymbolType type) const {
auto prefix = nid + "[";
auto suffix = fmt::format("[{}]", Common::EnumName(type).c_str());
for (const auto& symbol: m_symbols) {
if (Common::StartsWith(symbol.name, prefix) && Common::EndsWith(symbol.name, suffix)) {
return &symbol;
}
}
return nullptr;
}
const SymbolRecord* SymbolDatabase::FindByName(const std::string& name, SymbolType type) const {
auto prefix = name + "[";
auto suffix = fmt::format("[{}]", Common::EnumName(type).c_str());
for (const auto& symbol: m_symbols) {
if (Common::StartsWith(symbol.name, prefix) && Common::EndsWith(symbol.name, suffix)) {
return &symbol;
}
}
return nullptr;
}
} // namespace Loader
+62
View File
@@ -0,0 +1,62 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_SYMBOLDATABASE_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_SYMBOLDATABASE_H_
#include "common/abi.h"
#include "common/common.h"
#include "common/stringUtils.h"
#include <unordered_map>
#include <vector>
namespace Loader {
enum class SymbolType {
Unknown,
Func,
Object,
TlsModule,
NoType,
};
struct SymbolRecord {
std::string name;
std::string dbg_name;
uint64_t vaddr;
};
struct SymbolResolve {
std::string name;
std::string library;
int library_version;
std::string module;
int module_version_major;
int module_version_minor;
SymbolType type;
};
class SymbolDatabase {
public:
SymbolDatabase() = default;
virtual ~SymbolDatabase() = default;
void Add(const SymbolResolve& s, uint64_t vaddr);
void Add(const SymbolResolve& s, uint64_t vaddr, const std::string& dbg_name);
[[nodiscard]] const SymbolRecord* Find(const SymbolResolve& s) const;
[[nodiscard]] const SymbolRecord* FindByNid(const std::string& nid, SymbolType type) const;
[[nodiscard]] const SymbolRecord* FindByName(const std::string& name, SymbolType type) const;
void DbgDump(const std::string& folder, const std::string& file_name);
KYTY_CLASS_NO_COPY(SymbolDatabase);
static std::string GenerateName(const SymbolResolve& s);
private:
std::vector<SymbolRecord> m_symbols;
std::unordered_map<std::string, size_t> m_map;
};
} // namespace Loader
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_SYMBOLDATABASE_H_ */
+677
View File
@@ -0,0 +1,677 @@
#include "loader/systemContent.h"
#include "common/assert.h"
#include "common/byteBuffer.h"
#include "common/common.h"
#include "common/file.h"
#include "common/logging/log.h"
#include "common/singleton.h"
#include "common/threads.h"
#include <algorithm>
#include <charconv>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <nlohmann/json.hpp>
#include <string>
#include <string_view>
#include <vector>
namespace Loader {
class Psf {
public:
Psf() = default;
virtual ~Psf();
KYTY_CLASS_NO_COPY(Psf);
void Open(const std::filesystem::path& file_name);
void OpenChunkDefs(const std::filesystem::path& file_name);
void Close();
bool IsValid();
bool GetParamInt(const char* name, int32_t* value);
bool GetParamString(const char* name, std::string* value);
bool GetParamString(const char* name, char* value, size_t value_size);
uint64_t GetFlexibleMemorySize();
void DbgPrint();
private:
enum class ParamType {
Text,
Int,
};
struct Param {
std::string name;
ParamType type = ParamType::Text;
std::string string_value;
std::string string_utf8;
int32_t int_value = 0;
};
void OpenJson(const std::filesystem::path& file_name);
void AddParamString(const std::string& name, const std::string& value);
void AddParamInt(const std::string& name, int32_t value);
Param* FindParam(const char* name);
Common::Mutex m_mutex;
bool m_opened = false;
std::vector<Param> m_params;
uint64_t m_flexible_memory_size = 0;
};
class PlayGo {
public:
PlayGo() = default;
virtual ~PlayGo();
KYTY_CLASS_NO_COPY(PlayGo);
void Open(const std::filesystem::path& file_name);
void OpenChunkDefs(const std::filesystem::path& file_name);
void OpenChunkManifest(const std::filesystem::path& file_name);
void Close();
bool IsValid();
bool GetChunksNum(uint32_t* num);
void DbgPrint();
private:
Common::File m_f;
Common::Mutex m_mutex;
bool m_opened = false;
uint16_t m_chunks_num = 0;
};
struct SystemContent {
std::filesystem::path psf_path;
Psf psf;
std::filesystem::path playgo_path;
PlayGo playgo;
std::filesystem::path icon_path;
};
Psf::~Psf() {
Close();
}
void Psf::Open(const std::filesystem::path& file_name) {
Common::LockGuard lock(m_mutex);
Close();
OpenJson(file_name);
}
void Psf::OpenJson(const std::filesystem::path& file_name) {
Common::File f;
f.Open(file_name, Common::File::Mode::Read);
if (f.IsInvalid()) {
LOGF("Can't open %s\n", Common::PathToString(file_name).c_str());
return;
}
auto buf = f.ReadWholeBuffer();
if (buf.Size() == 0) {
LOGF("invalid json file: %s\n", Common::PathToString(file_name).c_str());
f.Close();
return;
}
std::string default_language;
std::vector<std::string> localized_languages;
std::vector<std::string> localized_titles;
const auto* json_begin = reinterpret_cast<const char*>(buf.GetDataConst());
const auto* json_end = json_begin + buf.Size();
auto param_json = nlohmann::ordered_json::parse(json_begin, json_end, nullptr, false);
if (param_json.is_discarded() || !param_json.is_object()) {
LOGF("invalid json file: %s\n", Common::PathToString(file_name).c_str());
f.Close();
return;
}
auto add_string_param = [&](const char* json_key, const char* param_key) {
auto it = param_json.find(json_key);
if (it != param_json.end() && it->is_string()) {
AddParamString(param_key, it->get_ref<const std::string&>());
}
};
auto add_int_param = [&](const char* json_key, const char* param_key) {
auto it = param_json.find(json_key);
if (it == param_json.end() || !it->is_number_integer()) {
return;
}
int64_t value = 0;
if (it->is_number_unsigned()) {
value = static_cast<int64_t>(it->get<uint64_t>());
} else {
value = it->get<int64_t>();
}
AddParamInt(param_key, static_cast<int32_t>(value));
};
add_string_param("titleId", "TITLE_ID");
add_string_param("contentId", "CONTENT_ID");
add_string_param("contentVersion", "APP_VER");
add_string_param("appVersion", "APP_VER");
add_int_param("userDefinedParam1", "USER_DEFINED_PARAM_1");
add_int_param("userDefinedParam2", "USER_DEFINED_PARAM_2");
add_int_param("userDefinedParam3", "USER_DEFINED_PARAM_3");
add_int_param("userDefinedParam4", "USER_DEFINED_PARAM_4");
if (auto kernel = param_json.find("kernel");
kernel != param_json.end() && kernel->is_object()) {
if (auto it = kernel->find("flexibleMemorySize"); it != kernel->end()) {
m_flexible_memory_size = it->get<uint64_t>();
}
}
auto localized = param_json.find("localizedParameters");
if (localized != param_json.end() && localized->is_object()) {
auto default_it = localized->find("defaultLanguage");
if (default_it != localized->end() && default_it->is_string()) {
default_language = default_it->get_ref<const std::string&>();
}
for (auto it = localized->begin(); it != localized->end(); ++it) {
if (!it->is_object()) {
continue;
}
auto title_it = it->find("titleName");
if (title_it != it->end() && title_it->is_string()) {
localized_languages.push_back(it.key());
localized_titles.push_back(title_it->get_ref<const std::string&>());
}
}
}
std::string title;
for (size_t i = 0; i < localized_titles.size(); i++) {
if (!default_language.empty() && localized_languages[i] == default_language) {
title = localized_titles[i];
break;
}
}
if (title.empty()) {
for (size_t i = 0; i < localized_titles.size(); i++) {
if (localized_languages[i] == "en-US") {
title = localized_titles[i];
break;
}
}
}
if (title.empty() && !localized_titles.empty()) {
title = localized_titles[0];
}
if (!title.empty()) {
AddParamString("TITLE", title);
}
f.Close();
m_opened = true;
}
void Psf::Close() {
Common::LockGuard lock(m_mutex);
m_params.clear();
m_flexible_memory_size = 0;
m_opened = false;
}
bool Psf::IsValid() {
Common::LockGuard lock(m_mutex);
return m_opened;
}
void Psf::DbgPrint() {
Common::LockGuard lock(m_mutex);
if (!m_opened) {
return;
}
for (const auto& param: m_params) {
LOGF("%s, ", param.name.c_str());
switch (param.type) {
case ParamType::Text: {
auto str = Common::ReplaceChar(param.string_value, '\n', ',');
LOGF("string = %s\n", str.c_str());
break;
}
case ParamType::Int:
LOGF("int = 0x%08" PRIx32 "\n", static_cast<uint32_t>(param.int_value));
break;
default: EXIT("unknown type\n");
}
}
}
Psf::Param* Psf::FindParam(const char* name) {
for (auto& param: m_params) {
if (param.name == name) {
return &param;
}
}
return nullptr;
}
void Psf::AddParamString(const std::string& name, const std::string& value) {
auto* param = FindParam(name.c_str());
if (param == nullptr) {
m_params.push_back({});
param = &m_params.back();
param->name = name;
}
param->type = ParamType::Text;
param->string_utf8 = value;
param->string_value = value;
}
void Psf::AddParamInt(const std::string& name, int32_t value) {
auto* param = FindParam(name.c_str());
if (param == nullptr) {
m_params.push_back({});
param = &m_params.back();
param->name = name;
}
param->type = ParamType::Int;
param->int_value = value;
}
bool Psf::GetParamInt(const char* name, int32_t* value) {
Common::LockGuard lock(m_mutex);
if (!m_opened) {
return false;
}
if (auto* param = FindParam(name); param != nullptr && param->type == ParamType::Int) {
*value = param->int_value;
return true;
}
return false;
}
bool Psf::GetParamString(const char* name, std::string* value) {
Common::LockGuard lock(m_mutex);
if (!m_opened) {
return false;
}
if (auto* param = FindParam(name); param != nullptr && param->type == ParamType::Text) {
*value = param->string_value;
return true;
}
return false;
}
bool Psf::GetParamString(const char* name, char* value, size_t value_size) {
Common::LockGuard lock(m_mutex);
if (!m_opened) {
return false;
}
if (auto* param = FindParam(name); param != nullptr && param->type == ParamType::Text &&
param->string_utf8.size() + 1 <= value_size) {
std::memcpy(value, param->string_utf8.c_str(), param->string_utf8.size() + 1);
return true;
}
return false;
}
uint64_t Psf::GetFlexibleMemorySize() {
Common::LockGuard lock(m_mutex);
return m_flexible_memory_size;
}
PlayGo::~PlayGo() {
Close();
}
void PlayGo::Open(const std::filesystem::path& file_name) {
Common::LockGuard lock(m_mutex);
Close();
m_f.Open(file_name, Common::File::Mode::Read);
if (m_f.IsInvalid()) {
LOGF("Can't open %s\n", Common::PathToString(file_name).c_str());
return;
}
uint32_t magic1 = 0;
m_f.Read(&magic1, 4);
if (magic1 != 0x6f676c70) {
LOGF("invalid file: magic1 = %08" PRIx32 "\n", magic1);
return;
}
m_f.Seek(10);
m_f.Read(&m_chunks_num, 2);
m_opened = true;
}
static bool ParseXmlUintAttr(std::string_view tag, std::string_view attr, uint32_t& value) {
const auto attr_pos = tag.find(attr);
if (attr_pos == std::string_view::npos) {
return false;
}
auto pos = attr_pos + attr.size();
while (pos < tag.size() && (tag[pos] == ' ' || tag[pos] == '\t')) {
pos++;
}
if (pos >= tag.size() || tag[pos] != '=') {
return false;
}
pos++;
while (pos < tag.size() && (tag[pos] == ' ' || tag[pos] == '\t')) {
pos++;
}
if (pos >= tag.size() || (tag[pos] != '"' && tag[pos] != '\'')) {
return false;
}
const char quote = tag[pos++];
const auto start = pos;
while (pos < tag.size() && tag[pos] >= '0' && tag[pos] <= '9') {
pos++;
}
if (start == pos || pos >= tag.size() || tag[pos] != quote) {
return false;
}
uint32_t parsed = 0;
const auto [ptr, ec] = std::from_chars(tag.data() + start, tag.data() + pos, parsed);
if (ec != std::errc {} || ptr != tag.data() + pos) {
return false;
}
value = parsed;
return true;
}
void PlayGo::OpenChunkDefs(const std::filesystem::path& file_name) {
Common::LockGuard lock(m_mutex);
if (m_opened) {
return;
}
Common::File f;
f.Open(file_name, Common::File::Mode::Read);
if (f.IsInvalid()) {
LOGF("Can't open %s\n", Common::PathToString(file_name).c_str());
return;
}
auto buf = f.ReadWholeBuffer();
f.Close();
if (buf.Size() == 0) {
LOGF("invalid file: %s\n", Common::PathToString(file_name).c_str());
return;
}
const std::string_view xml(reinterpret_cast<const char*>(buf.GetDataConst()), buf.Size());
uint32_t max_chunk_id = 0;
bool found = false;
if (const auto chunks_pos = xml.find("<chunks"); chunks_pos != std::string_view::npos) {
const auto tag_end = xml.find('>', chunks_pos);
if (tag_end != std::string_view::npos) {
uint32_t default_chunk = 0;
if (ParseXmlUintAttr(xml.substr(chunks_pos, tag_end - chunks_pos), "default_chunk",
default_chunk)) {
max_chunk_id = std::max(max_chunk_id, default_chunk);
found = true;
}
}
}
size_t pos = 0;
while ((pos = xml.find("<chunk", pos)) != std::string_view::npos) {
const auto name_end = pos + 6;
if (name_end >= xml.size() || !(xml[name_end] == ' ' || xml[name_end] == '\t' ||
xml[name_end] == '\r' || xml[name_end] == '\n')) {
pos = name_end;
continue;
}
const auto tag_end = xml.find('>', name_end);
if (tag_end == std::string_view::npos) {
break;
}
uint32_t chunk_id = 0;
if (ParseXmlUintAttr(xml.substr(pos, tag_end - pos), "id", chunk_id)) {
max_chunk_id = std::max(max_chunk_id, chunk_id);
found = true;
}
pos = tag_end + 1;
}
if (!found || max_chunk_id >= 1000) {
LOGF("invalid playgo chunk definitions: max_chunk_id=%" PRIu32 ", path=%s\n", max_chunk_id,
Common::PathToString(file_name).c_str());
return;
}
m_chunks_num = static_cast<uint16_t>(max_chunk_id + 1);
m_opened = true;
}
void PlayGo::OpenChunkManifest(const std::filesystem::path& file_name) {
Common::LockGuard lock(m_mutex);
if (m_opened) {
return;
}
Common::File f;
f.Open(file_name, Common::File::Mode::Read);
if (f.IsInvalid()) {
LOGF("Can't open %s\n", Common::PathToString(file_name).c_str());
return;
}
auto buf = f.ReadWholeBuffer();
f.Close();
constexpr uint8_t id_field[] = {0x08, 'i', 'd', 0x00};
bool chunks[1000] {};
uint32_t max_chunk_id = 0;
bool found = false;
const auto* data = buf.GetDataConst();
for (size_t pos = 0; pos + sizeof(id_field) + sizeof(uint32_t) <= buf.Size(); pos++) {
if (std::memcmp(data + pos, id_field, sizeof(id_field)) != 0) {
continue;
}
const auto* value = data + pos + sizeof(id_field);
const auto chunk_id =
static_cast<uint32_t>(value[0]) | (static_cast<uint32_t>(value[1]) << 8u) |
(static_cast<uint32_t>(value[2]) << 16u) | (static_cast<uint32_t>(value[3]) << 24u);
if (chunk_id < 1000) {
chunks[chunk_id] = true;
max_chunk_id = std::max(max_chunk_id, chunk_id);
found = true;
}
}
if (!found ||
!std::all_of(chunks, chunks + max_chunk_id + 1, [](bool chunk) { return chunk; })) {
LOGF("invalid chunk manifest: path=%s\n", Common::PathToString(file_name).c_str());
return;
}
m_chunks_num = static_cast<uint16_t>(max_chunk_id + 1);
m_opened = true;
LOGF("PlayGo: chunks num = %" PRIu16 " (from %s)\n", m_chunks_num,
Common::PathToString(file_name).c_str());
}
void PlayGo::Close() {
Common::LockGuard lock(m_mutex);
if (!m_f.IsInvalid()) {
m_f.Close();
}
m_opened = false;
m_chunks_num = 0;
}
bool PlayGo::IsValid() {
Common::LockGuard lock(m_mutex);
return m_opened;
}
bool PlayGo::GetChunksNum(uint32_t* num) {
Common::LockGuard lock(m_mutex);
if (m_opened) {
*num = m_chunks_num;
return true;
}
return false;
}
void PlayGo::DbgPrint() {
Common::LockGuard lock(m_mutex);
if (!m_opened) {
return;
}
LOGF("PlayGo: chunks num = %" PRIu16 "\n", m_chunks_num);
}
void SystemContentLoadParamSfo(const std::filesystem::path& file_name) {
auto* sc = Common::Singleton<SystemContent>::Instance();
if (!Common::File::IsFileExisting(file_name)) {
EXIT("Can't find file: %s\n", Common::PathToString(file_name).c_str());
}
sc->psf.Open(file_name);
if (!sc->psf.IsValid()) {
EXIT("invalid file: %s\n", Common::PathToString(file_name).c_str());
}
sc->psf_path = file_name;
sc->psf.DbgPrint();
sc->icon_path = file_name.parent_path() / "icon0.png";
if (!Common::File::IsFileExisting(sc->icon_path)) {
sc->icon_path.clear();
}
sc->playgo_path = file_name.parent_path() / "playgo-chunk.dat";
sc->playgo.Open(sc->playgo_path);
if (!sc->playgo.IsValid()) {
sc->playgo_path = file_name.parent_path().parent_path() / "playgo-chunkdefs.xml";
sc->playgo.OpenChunkDefs(sc->playgo_path);
}
if (!sc->playgo.IsValid()) {
sc->playgo_path = file_name.parent_path().parent_path() / "Data" / "chunkmanifest";
sc->playgo.OpenChunkManifest(sc->playgo_path);
}
if (sc->playgo.IsValid()) {
sc->playgo.DbgPrint();
}
}
bool SystemContentParamSfoGetInt(const char* name, int32_t* value) {
if (name == nullptr || value == nullptr) {
return false;
}
auto* sc = Common::Singleton<SystemContent>::Instance();
return sc->psf.GetParamInt(name, value);
}
bool SystemContentParamSfoGetString(const char* name, std::string* value) {
if (name == nullptr || value == nullptr) {
return false;
}
auto* sc = Common::Singleton<SystemContent>::Instance();
return sc->psf.GetParamString(name, value);
}
bool SystemContentParamSfoGetString(const char* name, char* value, size_t value_size) {
if (name == nullptr || value == nullptr) {
return false;
}
auto* sc = Common::Singleton<SystemContent>::Instance();
return sc->psf.GetParamString(name, value, value_size);
}
uint64_t SystemContentGetFlexibleMemorySize() {
auto* sc = Common::Singleton<SystemContent>::Instance();
return sc->psf.GetFlexibleMemorySize();
}
bool SystemContentGetIconPath(std::string* path) {
if (path == nullptr) {
return false;
}
auto* sc = Common::Singleton<SystemContent>::Instance();
if (sc->icon_path.empty()) {
return false;
}
*path = Common::PathToString(sc->icon_path);
return true;
}
bool SystemContentGetChunksNum(uint32_t* num) {
if (num == nullptr) {
return false;
}
auto* sc = Common::Singleton<SystemContent>::Instance();
return sc->playgo.GetChunksNum(num);
}
} // namespace Loader
+23
View File
@@ -0,0 +1,23 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_SYSTEMCONTENT_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_SYSTEMCONTENT_H_
#include "common/abi.h"
#include "common/common.h"
#include "common/stringUtils.h"
#include <cstdint>
#include <filesystem>
namespace Loader {
void SystemContentLoadParamSfo(const std::filesystem::path& file_name);
bool SystemContentParamSfoGetInt(const char* name, int32_t* value);
bool SystemContentParamSfoGetString(const char* name, std::string* value);
bool SystemContentParamSfoGetString(const char* name, char* value, size_t value_size);
uint64_t SystemContentGetFlexibleMemorySize();
bool SystemContentGetIconPath(std::string* path);
bool SystemContentGetChunksNum(uint32_t* num);
} // namespace Loader
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_SYSTEMCONTENT_H_ */
+40
View File
@@ -0,0 +1,40 @@
#include "common/timer.h"
#include "common/abi.h"
#include "common/dateTime.h"
#include "common/subsystems.h"
#include "loader/timer.h"
namespace Loader::Timer {
static Common::Timer g_timer;
KYTY_SUBSYSTEM_INIT(Timer) {
Start();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Timer) {}
KYTY_SUBSYSTEM_DESTROY(Timer) {}
void Start() {
g_timer.Start();
}
double GetTimeMs() {
return g_timer.GetTimeMs();
}
Common::Time GetTime() {
return Common::Time(static_cast<int>(GetTimeMs()));
}
uint64_t GetCounter() {
return g_timer.GetTicks();
}
uint64_t GetFrequency() {
return g_timer.GetFrequency();
}
} // namespace Loader::Timer
+21
View File
@@ -0,0 +1,21 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_TIMER_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_TIMER_H_
#include "common/abi.h"
#include "common/common.h"
#include "common/dateTime.h"
#include "common/subsystems.h"
namespace Loader::Timer {
KYTY_SUBSYSTEM_DEFINE(Timer);
void Start();
double GetTimeMs();
Common::Time GetTime();
uint64_t GetCounter();
uint64_t GetFrequency();
} // namespace Loader::Timer
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_TIMER_H_ */
+155
View File
@@ -0,0 +1,155 @@
#include "loader/x64InstructionEmulator.h"
#include "common/common.h"
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
#include <windows.h> // IWYU pragma: keep
#endif
namespace Loader::X64InstructionEmulator {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
static M128A* GetContextXmm(PCONTEXT context, uint8_t index) {
if (context == nullptr || index >= 16) {
return nullptr;
}
return &context->Xmm0 + index;
}
static uint64_t ExtractBitField(uint64_t value, uint32_t length, uint32_t index) {
length &= 0x3fu;
index &= 0x3fu;
if (length == 0) {
length = 64;
}
if (index >= 64) {
return 0;
}
auto available = 64u - index;
if (length > available) {
length = available;
}
const uint64_t mask = (length == 64 ? UINT64_MAX : ((uint64_t {1} << length) - 1u));
return (value >> index) & mask;
}
static uint64_t InsertBitField(uint64_t dst, uint64_t src, uint32_t length, uint32_t index) {
length &= 0x3fu;
index &= 0x3fu;
if (length == 0) {
length = 64;
}
if (index >= 64) {
return dst;
}
auto available = 64u - index;
if (length > available) {
length = available;
}
const uint64_t mask = (length == 64 ? UINT64_MAX : ((uint64_t {1} << length) - 1u));
const uint64_t shifted = (index == 0 ? mask : (mask << index));
const uint64_t src_shifted = (src & mask) << index;
return (dst & ~shifted) | src_shifted;
}
static bool TryEmulateSse4a(PCONTEXT context) {
if (context == nullptr) {
return false;
}
const auto* rip = reinterpret_cast<const uint8_t*>(context->Rip);
const uint8_t prefix = rip[0];
if (prefix != 0x66 && prefix != 0xf2) {
return false;
}
size_t offset = 1;
uint8_t rex = 0;
if ((rip[offset] & 0xf0u) == 0x40u) {
rex = rip[offset];
offset++;
}
if (rip[offset] != 0x0f || rip[offset + 1] != 0x78) {
return false;
}
auto modrm = rip[offset + 2];
if ((modrm & 0xc0u) != 0xc0u) {
return false;
}
const uint8_t reg = ((modrm >> 3u) & 0x07u) | ((rex & 0x04u) << 1u);
const uint8_t rm = (modrm & 0x07u) | ((rex & 0x01u) << 3u);
const uint8_t length = rip[offset + 3];
const uint8_t index = rip[offset + 4];
// AMD SSE4a immediate-form EXTRQ/INSERTQ. PS5 code can execute these natively on AMD hardware,
// while Intel hosts raise an illegal-instruction exception.
if (prefix == 0x66) {
auto* dst = GetContextXmm(context, rm);
if (dst == nullptr) {
return false;
}
dst->Low = ExtractBitField(dst->Low, length, index);
dst->High = 0;
context->Rip += offset + 5;
return true;
}
auto* dst = GetContextXmm(context, reg);
auto* src = GetContextXmm(context, rm);
if (dst == nullptr || src == nullptr) {
return false;
}
dst->Low = InsertBitField(dst->Low, src->Low, length, index);
context->Rip += offset + 5;
return true;
}
static bool TryEmulateMonitorxMwaitx(PCONTEXT context) {
if (context == nullptr) {
return false;
}
const auto* rip = reinterpret_cast<const uint8_t*>(context->Rip);
if (rip[0] != 0x0f || rip[1] != 0x01 || (rip[2] != 0xfa && rip[2] != 0xfb)) {
return false;
}
// AMD MONITORX/MWAITX are used by PS5 code in wait loops. Intel hosts can raise an illegal-
// instruction exception, so approximate them as a no-op/yield pair.
if (rip[2] == 0xfb) {
SwitchToThread();
}
context->Rip += 3;
return true;
}
#endif
bool TryEmulate(void* native_context) {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
auto* context = static_cast<PCONTEXT>(native_context);
return TryEmulateMonitorxMwaitx(context) || TryEmulateSse4a(context);
#else
(void)native_context;
return false;
#endif
}
} // namespace Loader::X64InstructionEmulator
+10
View File
@@ -0,0 +1,10 @@
#ifndef KYTY_LOADER_X64_INSTRUCTION_EMULATOR_H_
#define KYTY_LOADER_X64_INSTRUCTION_EMULATOR_H_
namespace Loader::X64InstructionEmulator {
[[nodiscard]] bool TryEmulate(void* native_context);
} // namespace Loader::X64InstructionEmulator
#endif /* KYTY_LOADER_X64_INSTRUCTION_EMULATOR_H_ */