load param.sfo

This commit is contained in:
InoriRus
2022-05-31 16:18:55 +10:00
parent 50b54b2607
commit 877560b7f6
54 changed files with 10562 additions and 245 deletions
@@ -0,0 +1,309 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_ELF_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_ELF_H_
#include "Kyty/Core/Common.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Common.h"
namespace Kyty::Core {
class File;
} // namespace Kyty::Core
#ifdef KYTY_EMU_ENABLED
namespace Kyty::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 String& file_name);
void Save(const String& file_name);
void DbgDump(const String& folder);
const char* GetSectionName(int index) { return m_str_table + m_shdr[index].sh_name; }
[[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]] 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 static_cast<Elf64_Dyn*>(m_dynamic); }
[[nodiscard]] const Elf64_Ehdr* GetEhdr() const { return m_ehdr; }
[[nodiscard]] const Elf64_Phdr* GetPhdr() const { return m_phdr; }
[[nodiscard]] const Elf64_Shdr* GetShdr() const { return m_shdr; }
[[nodiscard]] char* GetStrTable() const { return m_str_table; }
template <class T>
[[nodiscard]] T GetDynamicData(uint64_t offset) const
{
return (m_dynamic_data == nullptr ? nullptr : reinterpret_cast<T>(static_cast<uint8_t*>(m_dynamic_data) + offset));
}
private:
void Clear();
Core::File* m_f = nullptr;
SelfHeader* m_self = nullptr;
SelfSegment* m_self_segments = nullptr;
Elf64_Ehdr* m_ehdr = nullptr;
Elf64_Phdr* m_phdr = nullptr;
Elf64_Shdr* m_shdr = nullptr;
void* m_dynamic = nullptr;
void* m_dynamic_data = nullptr;
char* m_str_table = nullptr;
// uint64_t m_base_vaddr = 0;
};
} // namespace Kyty::Loader
#endif // KYTY_EMU_ENABLED
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_ELF_H_ */
@@ -0,0 +1,150 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_JIT_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_JIT_H_
#include "Emulator/Common.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::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;
}
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 SafeCall
{
using func_t = KYTY_MS_ABI uint8_t* (*)();
void SetFunc(func_t func) { *reinterpret_cast<func_t*>(&code[0x22]) = func; }
void SetRegSaveArea(uint8_t* area) { *reinterpret_cast<uint8_t**>(&code[0x18]) = area; }
void SetLockVar(uint8_t* lock_var) { *reinterpret_cast<uint8_t**>(&code[0x0e]) = lock_var; }
static uint64_t GetSize() { return 0x1000; }
uint8_t code[0x6c] = {
/*00*/ 0x51, // push rcx /* Save general purpose registers */
/*01*/ 0x52, // push rdx
/*02*/ 0x41, 0x50, // push r8
/*04*/ 0x41, 0x51, // push r9
/*06*/ 0x41, 0x52, // push r10
/*08*/ 0x41, 0x53, // push r11
/*0a*/ 0x57, // push rdi
/*0b*/ 0x56, // push rsi
/*0c*/ 0x48, 0xbf, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // movabs rdi,0x1122334455667788
/*16*/ 0x48, 0xbe, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // movabs rsi,0x1122334455667788
/*20*/ 0x48, 0xb9, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // movabs rcx,0x1122334455667788
/*2a*/ 0xb0, 0x01, // mov al,0x1 /* Lock */
/*2c*/ 0x86, 0x07, // xchg BYTE PTR [rdi],al
/*2e*/ 0x84, 0xc0, // test al,al
/*30*/ 0x75, 0xf8, // jne 2a <LOCK>
/*32*/ 0xb8, 0xff, 0xff, 0xff, 0xff, // mov eax,0xffffffff
/*37*/ 0xba, 0xff, 0xff, 0xff, 0xff, // mov edx,0xffffffff
/*3c*/ 0x0f, 0xae, 0x26, // xsave [rsi] /* Save float registers */
/*3f*/ 0x48, 0x83, 0xec, 0x08, // sub rsp,0x8 /* Align stack */
/*43*/ 0xff, 0xd1, // call rcx
/*45*/ 0x48, 0x83, 0xc4, 0x08, // add rsp,0x8
/*49*/ 0x48, 0x89, 0xc1, // mov rcx,rax
/*4c*/ 0xb8, 0xff, 0xff, 0xff, 0xff, // mov eax,0xffffffff
/*51*/ 0xba, 0xff, 0xff, 0xff, 0xff, // mov edx,0xffffffff
/*56*/ 0x0f, 0xae, 0x2e, // xrstor [rsi] /* Restore float registers */
/*59*/ 0x48, 0x89, 0xc8, // mov rax,rcx
/*5c*/ 0xc6, 0x07, 0x00, // mov BYTE PTR [rdi],0x0 /* Unlock */
/*5f*/ 0x5e, // pop rsi /* Restore general purpose registers */
/*60*/ 0x5f, // pop rdi
/*61*/ 0x41, 0x5b, // pop r11
/*63*/ 0x41, 0x5a, // pop r10
/*65*/ 0x41, 0x59, // pop r9
/*67*/ 0x41, 0x58, // pop r8
/*69*/ 0x5a, // pop rdx
/*6a*/ 0x59, // pop rcx
/*6b*/ 0xc3, // ret
};
};
#pragma pack()
} // namespace Kyty::Loader::Jit
#endif // KYTY_EMU_ENABLED
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_JIT_H_ */
@@ -0,0 +1,27 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_PARAM_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_PARAM_H_
#include "Kyty/Core/Common.h"
#include "Kyty/Core/String.h"
#include "Emulator/Common.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Libs::Graphics {
class Image;
} // namespace Kyty::Libs::Graphics
namespace Kyty::Loader {
void ParamSfoLoad(const String& file_name);
bool ParamSfoGetInt(const char* name, int32_t* value);
bool ParamSfoGetString(const char* name, String* value);
bool ParamSfoGetString(const char* name, char* value, size_t value_size);
Libs::Graphics::Image* ParamSfoGetIcon();
} // namespace Kyty::Loader
#endif // KYTY_EMU_ENABLED
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_PARAM_H_ */
@@ -0,0 +1,189 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_RUNTIMELINKER_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_RUNTIMELINKER_H_
#include "Kyty/Core/Common.h"
#include "Kyty/Core/Hashmap.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Common.h"
#include "Emulator/Loader/SymbolDatabase.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Loader {
class Elf64;
struct Elf64_Sym;
struct Elf64_Rela;
class RuntimeLinker;
namespace VirtualMemory {
class ExceptionHandler;
} // namespace VirtualMemory
using module_func_t = int (*)(size_t args, const void* argp);
struct ModuleId
{
bool operator==(const ModuleId& other) const
{
return version_major == other.version_major && version_minor == other.version_minor && name == other.name;
}
String id;
int version_major;
int version_minor;
String name;
};
struct LibraryId
{
bool operator==(const LibraryId& other) const { return version == other.version && name == other.name; }
String id;
int version;
String name;
};
struct ThreadLocalStorage
{
uint64_t image_vaddr = 0;
uint64_t image_size = 0;
uint64_t handler_vaddr = 0;
Core::Hashmap<int, uint8_t*> tlss;
Core::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;
Vector<const char*> needed;
Vector<ModuleId> export_modules;
Vector<ModuleId> import_modules;
Vector<LibraryId> export_libs;
Vector<LibraryId> import_libs;
};
struct Program
{
int32_t unique_id = -1;
RuntimeLinker* rt = nullptr;
String file_name;
Elf64* elf = nullptr;
VirtualMemory::ExceptionHandler* exception_handler = nullptr;
DynamicInfo* dynamic_info = nullptr;
uint64_t base_vaddr = 0;
uint64_t base_size = 0;
uint64_t base_size_aligned = 0;
SymbolDatabase* export_symbols = nullptr;
SymbolDatabase* import_symbols = nullptr;
ThreadLocalStorage tls;
bool fail_if_global_not_resolved = true;
bool dbg_print_reloc = 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 String& folder);
Program* LoadProgram(const String& elf_name);
void SaveMainProgram(const String& elf_name);
void SaveProgram(Program* program, const String& elf_name);
void UnloadProgram(Program* program);
[[nodiscard]] uint64_t GetEntry();
[[nodiscard]] uint64_t GetProcParam();
void RelocateAll();
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 Resolve(const String& name, SymbolType type, Program* program, SymbolRecord* out_info, bool* bind_self);
SymbolDatabase* Symbols() { return m_symbols; }
static uint64_t ReadFromElf(Program* program, uint64_t vaddr);
Program* FindProgramByAddr(uint64_t vaddr);
Program* FindProgramById(int32_t id);
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);
Program* FindProgram(const ModuleId& m, const LibraryId& l);
static const ModuleId* FindModule(const Program& program, const String& id);
static const LibraryId* FindLibrary(const Program& program, const String& id);
Vector<Program*> m_programs;
SymbolDatabase* m_symbols = nullptr;
bool m_relocated = false;
Core::Mutex m_mutex;
};
} // namespace Kyty::Loader
#endif // KYTY_EMU_ENABLED
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_RUNTIMELINKER_H_ */
@@ -0,0 +1,68 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_SYMBOLDATABASE_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_SYMBOLDATABASE_H_
#include "Kyty/Core/Common.h"
#include "Kyty/Core/Hashmap.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Vector.h"
#include "Emulator/Common.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Loader {
enum class SymbolType
{
Unknown,
Func,
Object,
TlsModule,
NoType,
};
struct SymbolRecord
{
String name;
String dbg_name;
uint64_t vaddr;
};
struct SymbolResolve
{
String name;
String library;
int library_version;
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 String& dbg_name);
[[nodiscard]] const SymbolRecord* Find(const SymbolResolve& s) const;
void DbgDump(const String& folder, const String& file_name);
KYTY_CLASS_NO_COPY(SymbolDatabase);
static String GenerateName(const SymbolResolve& s);
private:
Vector<SymbolRecord> m_symbols;
Core::Hashmap<String, uint32_t> m_map;
};
} // namespace Kyty::Loader
#endif // KYTY_EMU_ENABLED
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_SYMBOLDATABASE_H_ */
@@ -0,0 +1,26 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_TIMER_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_TIMER_H_
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DateTime.h"
#include "Kyty/Core/Subsystems.h"
#include "Emulator/Common.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Loader::Timer {
KYTY_SUBSYSTEM_DEFINE(Timer);
void Start();
double GetTimeMs();
Core::Time GetTime();
uint64_t GetCounter();
uint64_t GetFrequency();
} // namespace Kyty::Loader::Timer
#endif // KYTY_EMU_ENABLED
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_TIMER_H_ */
@@ -0,0 +1,115 @@
#ifndef EMULATOR_INCLUDE_EMULATOR_LOADER_VIRTUALMEMORY_H_
#define EMULATOR_INCLUDE_EMULATOR_LOADER_VIRTUALMEMORY_H_
#include "Kyty/Core/Common.h"
#include "Kyty/Core/String.h"
#include "Emulator/Common.h"
#ifdef KYTY_EMU_ENABLED
namespace Kyty::Loader {
enum class ProcessorArchitecture
{
Unknown,
Amd64, // x64 (AMD or Intel)
};
struct SystemInfo
{
uint32_t PageSize;
uint64_t MinimumApplicationAddress;
uint64_t MaximumApplicationAddress;
uint32_t ActiveProcessorMask;
uint32_t NumberOfProcessors;
ProcessorArchitecture ProcessorArchitecture;
uint32_t AllocationGranularity;
uint16_t ProcessorLevel;
uint16_t ProcessorRevision;
String ProcessorName;
};
SystemInfo GetSystemInfo();
namespace VirtualMemory {
class ExceptionHandlerPrivate;
class ExceptionHandler
{
public:
enum class ExceptionType
{
Unknown,
AccessViolation
};
enum class AccessViolationType
{
Unknown,
Read,
Write,
Execute
};
struct ExceptionInfo
{
ExceptionType type = ExceptionType::Unknown;
AccessViolationType access_violation_type = AccessViolationType::Unknown;
uint64_t access_violation_vaddr = 0;
uint64_t exception_address = 0;
uint64_t rbp = 0;
};
using handler_func_t = void (*)(const ExceptionInfo*);
ExceptionHandler();
virtual ~ExceptionHandler();
KYTY_CLASS_NO_COPY(ExceptionHandler);
static uint64_t GetSize();
bool Install(uint64_t base_address, uint64_t handler_addr, uint64_t image_size, handler_func_t func);
bool Uninstall();
static bool InstallVectored(handler_func_t func);
private:
ExceptionHandlerPrivate* m_p = nullptr;
};
enum class Mode : uint32_t
{
NoAccess = 0,
Read = 1,
Write = 2,
ReadWrite = Read | Write,
Execute = 4,
ExecuteRead = Execute | Read,
ExecuteWrite = Execute | Write,
ExecuteReadWrite = Execute | Read | Write,
};
inline bool IsExecute(Mode mode)
{
return (mode == Mode::Execute || mode == Mode::ExecuteRead || mode == Mode::ExecuteWrite || mode == Mode::ExecuteReadWrite);
}
void Init();
uint64_t Alloc(uint64_t address, uint64_t size, Mode mode);
uint64_t AllocAligned(uint64_t address, uint64_t size, Mode mode, uint64_t alignment);
bool Free(uint64_t address);
bool Protect(uint64_t address, uint64_t size, Mode mode, Mode* old_mode = nullptr);
bool FlushInstructionCache(uint64_t address, uint64_t size);
bool PatchReplace(uint64_t vaddr, uint64_t value);
} // namespace VirtualMemory
} // namespace Kyty::Loader
#endif // KYTY_EMU_ENABLED
#endif /* EMULATOR_INCLUDE_EMULATOR_LOADER_VIRTUALMEMORY_H_ */