more work on relocations for libc loading

This commit is contained in:
georgemoralis 2023-12-31 19:47:38 +02:00
parent 7dfdf4c259
commit 62b2e57b6b
3 changed files with 359 additions and 437 deletions

View File

@ -1,11 +1,13 @@
#include <fmt/core.h> #include "core/linker.h"
#include <Zydis/Zydis.h> #include <Zydis/Zydis.h>
#include <fmt/core.h>
#include "common/log.h" #include "common/log.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/aerolib/aerolib.h" #include "core/aerolib/aerolib.h"
#include "core/aerolib/stubs.h" #include "core/aerolib/stubs.h"
#include "core/hle/libraries/libkernel/thread_management.h" #include "core/hle/libraries/libkernel/thread_management.h"
#include "core/linker.h"
#include "core/virtual_memory.h" #include "core/virtual_memory.h"
namespace Core { namespace Core {
@ -14,21 +16,16 @@ constexpr bool debug_loader = true;
static u64 g_load_addr = SYSTEM_RESERVED + CODE_BASE_OFFSET; static u64 g_load_addr = SYSTEM_RESERVED + CODE_BASE_OFFSET;
static u64 get_aligned_size(const elf_program_header& phdr) static u64 get_aligned_size(const elf_program_header& phdr) {
{
return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1) : phdr.p_memsz); return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1) : phdr.p_memsz);
} }
static u64 calculate_base_size(const elf_header& ehdr, std::span<const elf_program_header> phdr) static u64 calculate_base_size(const elf_header& ehdr, std::span<const elf_program_header> phdr) {
{
u64 base_size = 0; u64 base_size = 0;
for (u16 i = 0; i < ehdr.e_phnum; i++) for (u16 i = 0; i < ehdr.e_phnum; i++) {
{ if (phdr[i].p_memsz != 0 && (phdr[i].p_type == PT_LOAD || phdr[i].p_type == PT_SCE_RELRO)) {
if (phdr[i].p_memsz != 0 && (phdr[i].p_type == PT_LOAD || phdr[i].p_type == PT_SCE_RELRO))
{
u64 last_addr = phdr[i].p_vaddr + get_aligned_size(phdr[i]); u64 last_addr = phdr[i].p_vaddr + get_aligned_size(phdr[i]);
if (last_addr > base_size) if (last_addr > base_size) {
{
base_size = last_addr; base_size = last_addr;
} }
} }
@ -36,23 +33,16 @@ static u64 calculate_base_size(const elf_header& ehdr, std::span<const elf_progr
return base_size; return base_size;
} }
static std::string encodeId(u64 nVal) static std::string encodeId(u64 nVal) {
{
std::string enc; std::string enc;
const char pCodes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; const char pCodes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
if (nVal < 0x40u) if (nVal < 0x40u) {
{
enc += pCodes[nVal]; enc += pCodes[nVal];
} } else {
else if (nVal < 0x1000u) {
{
if (nVal < 0x1000u)
{
enc += pCodes[static_cast<u16>(nVal >> 6u) & 0x3fu]; enc += pCodes[static_cast<u16>(nVal >> 6u) & 0x3fu];
enc += pCodes[nVal & 0x3fu]; enc += pCodes[nVal & 0x3fu];
} } else {
else
{
enc += pCodes[static_cast<u16>(nVal >> 12u) & 0x3fu]; enc += pCodes[static_cast<u16>(nVal >> 12u) & 0x3fu];
enc += pCodes[static_cast<u16>(nVal >> 6u) & 0x3fu]; enc += pCodes[static_cast<u16>(nVal >> 6u) & 0x3fu];
enc += pCodes[nVal & 0x3fu]; enc += pCodes[nVal & 0x3fu];
@ -65,8 +55,7 @@ Linker::Linker() = default;
Linker::~Linker() = default; Linker::~Linker() = default;
Module* Linker::LoadModule(const std::string& elf_name) Module* Linker::LoadModule(const std::string& elf_name) {
{
std::scoped_lock lock{m_mutex}; std::scoped_lock lock{m_mutex};
auto& m = m_modules.emplace_back(); auto& m = m_modules.emplace_back();
@ -80,414 +69,335 @@ Module* Linker::LoadModule(const std::string& elf_name)
Relocate(&m); Relocate(&m);
} else { } else {
m_modules.pop_back(); m_modules.pop_back();
return nullptr; // It is not a valid elf file //TODO check it why! return nullptr; // It is not a valid elf file //TODO check it why!
} }
return &m; return &m;
} }
Module* Linker::FindModule(/*u32 id*/) void Linker::LoadModuleToMemory(Module* m) {
{ // get elf header, program header
// TODO atm we only have 1 module so we don't need to iterate on vector
if (m_modules.empty()) [[unlikely]] {
return nullptr;
}
return &m_modules[0];
}
void Linker::LoadModuleToMemory(Module* m)
{
//get elf header, program header
const auto elf_header = m->elf.GetElfHeader(); const auto elf_header = m->elf.GetElfHeader();
const auto elf_pheader = m->elf.GetProgramHeader(); const auto elf_pheader = m->elf.GetProgramHeader();
u64 base_size = calculate_base_size(elf_header, elf_pheader); u64 base_size = calculate_base_size(elf_header, elf_pheader);
m->aligned_base_size = (base_size & ~(static_cast<u64>(0x1000) - 1)) + 0x1000;//align base size to 0x1000 block size (TODO is that the default block size or it can be changed? m->aligned_base_size = (base_size & ~(static_cast<u64>(0x1000) - 1)) +
0x1000; // align base size to 0x1000 block size (TODO is that the default block size or it can be changed?
m->base_virtual_addr = VirtualMemory::memory_alloc(g_load_addr, m->aligned_base_size, VirtualMemory::MemoryMode::ExecuteReadWrite); m->base_virtual_addr = VirtualMemory::memory_alloc(g_load_addr, m->aligned_base_size, VirtualMemory::MemoryMode::ExecuteReadWrite);
LOG_INFO_IF(debug_loader, "====Load Module to Memory ========\n"); LOG_INFO_IF(debug_loader, "====Load Module to Memory ========\n");
LOG_INFO_IF(debug_loader, "base_virtual_addr ......: {:#018x}\n", m->base_virtual_addr); LOG_INFO_IF(debug_loader, "base_virtual_addr ......: {:#018x}\n", m->base_virtual_addr);
LOG_INFO_IF(debug_loader, "base_size ..............: {:#018x}\n", base_size); LOG_INFO_IF(debug_loader, "base_size ..............: {:#018x}\n", base_size);
LOG_INFO_IF(debug_loader, "aligned_base_size ......: {:#018x}\n", m->aligned_base_size); LOG_INFO_IF(debug_loader, "aligned_base_size ......: {:#018x}\n", m->aligned_base_size);
for (u16 i = 0; i < elf_header.e_phnum; i++) for (u16 i = 0; i < elf_header.e_phnum; i++) {
{ switch (elf_pheader[i].p_type) {
switch (elf_pheader[i].p_type) case PT_LOAD:
{ case PT_SCE_RELRO:
case PT_LOAD: if (elf_pheader[i].p_memsz != 0) {
case PT_SCE_RELRO: u64 segment_addr = elf_pheader[i].p_vaddr + m->base_virtual_addr;
if (elf_pheader[i].p_memsz != 0) u64 segment_file_size = elf_pheader[i].p_filesz;
{
u64 segment_addr = elf_pheader[i].p_vaddr + m->base_virtual_addr;
u64 segment_file_size = elf_pheader[i].p_filesz;
u64 segment_memory_size = get_aligned_size(elf_pheader[i]); u64 segment_memory_size = get_aligned_size(elf_pheader[i]);
auto segment_mode = m->elf.ElfPheaderFlagsStr(elf_pheader[i].p_flags); auto segment_mode = m->elf.ElfPheaderFlagsStr(elf_pheader[i].p_flags);
LOG_INFO_IF(debug_loader, "program header = [{}] type = {}\n",i,m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); LOG_INFO_IF(debug_loader, "program header = [{}] type = {}\n", i, m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
LOG_INFO_IF(debug_loader, "segment_addr ..........: {:#018x}\n", segment_addr); LOG_INFO_IF(debug_loader, "segment_addr ..........: {:#018x}\n", segment_addr);
LOG_INFO_IF(debug_loader, "segment_file_size .....: {}\n", segment_file_size); LOG_INFO_IF(debug_loader, "segment_file_size .....: {}\n", segment_file_size);
LOG_INFO_IF(debug_loader, "segment_memory_size ...: {}\n", segment_memory_size); LOG_INFO_IF(debug_loader, "segment_memory_size ...: {}\n", segment_memory_size);
LOG_INFO_IF(debug_loader, "segment_mode ..........: {}\n", segment_mode); LOG_INFO_IF(debug_loader, "segment_mode ..........: {}\n", segment_mode);
m->elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, segment_file_size); m->elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, segment_file_size);
} } else {
else
{
LOG_ERROR_IF(debug_loader, "p_memsz==0 in type {}\n", m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); LOG_ERROR_IF(debug_loader, "p_memsz==0 in type {}\n", m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
} }
break; break;
case PT_DYNAMIC: case PT_DYNAMIC:
if (elf_pheader[i].p_filesz != 0) if (elf_pheader[i].p_filesz != 0) {
{
m->m_dynamic.resize(elf_pheader[i].p_filesz); m->m_dynamic.resize(elf_pheader[i].p_filesz);
m->elf.LoadSegment(reinterpret_cast<u64>(m->m_dynamic.data()), elf_pheader[i].p_offset, elf_pheader[i].p_filesz); m->elf.LoadSegment(reinterpret_cast<u64>(m->m_dynamic.data()), elf_pheader[i].p_offset, elf_pheader[i].p_filesz);
} } else {
else
{
LOG_ERROR_IF(debug_loader, "p_filesz==0 in type {}\n", m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); LOG_ERROR_IF(debug_loader, "p_filesz==0 in type {}\n", m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
} }
break; break;
case PT_SCE_DYNLIBDATA: case PT_SCE_DYNLIBDATA:
if (elf_pheader[i].p_filesz != 0) if (elf_pheader[i].p_filesz != 0) {
{
m->m_dynamic_data.resize(elf_pheader[i].p_filesz); m->m_dynamic_data.resize(elf_pheader[i].p_filesz);
m->elf.LoadSegment(reinterpret_cast<u64>(m->m_dynamic_data.data()), elf_pheader[i].p_offset, elf_pheader[i].p_filesz); m->elf.LoadSegment(reinterpret_cast<u64>(m->m_dynamic_data.data()), elf_pheader[i].p_offset, elf_pheader[i].p_filesz);
} } else {
else
{
LOG_ERROR_IF(debug_loader, "p_filesz==0 in type {}\n", m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); LOG_ERROR_IF(debug_loader, "p_filesz==0 in type {}\n", m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
} }
break; break;
default: default: LOG_ERROR_IF(debug_loader, "Unimplemented type {}\n", m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type));
LOG_ERROR_IF(debug_loader, "Unimplemented type {}\n", m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); }
} }
}
LOG_INFO_IF(debug_loader, "program entry addr ..........: {:#018x}\n", m->elf.GetElfEntry() + m->base_virtual_addr); LOG_INFO_IF(debug_loader, "program entry addr ..........: {:#018x}\n", m->elf.GetElfEntry() + m->base_virtual_addr);
auto* rt1 = reinterpret_cast<uint8_t*>(m->elf.GetElfEntry() + m->base_virtual_addr); auto* rt1 = reinterpret_cast<uint8_t*>(m->elf.GetElfEntry() + m->base_virtual_addr);
ZyanU64 runtime_address = m->elf.GetElfEntry() + m->base_virtual_addr; ZyanU64 runtime_address = m->elf.GetElfEntry() + m->base_virtual_addr;
// Loop over the instructions in our buffer. // Loop over the instructions in our buffer.
ZyanUSize offset = 0; ZyanUSize offset = 0;
ZydisDisassembledInstruction instruction; ZydisDisassembledInstruction instruction;
while (ZYAN_SUCCESS(ZydisDisassembleIntel( while (ZYAN_SUCCESS(ZydisDisassembleIntel(
/* machine_mode: */ ZYDIS_MACHINE_MODE_LONG_64, /* machine_mode: */ ZYDIS_MACHINE_MODE_LONG_64,
/* runtime_address: */ runtime_address, /* runtime_address: */ runtime_address,
/* buffer: */ rt1 + offset, /* buffer: */ rt1 + offset,
/* length: */ sizeof(rt1) - offset, /* length: */ sizeof(rt1) - offset,
/* instruction: */ &instruction /* instruction: */ &instruction))) {
))) { fmt::print("{:#x}" PRIX64 " {}\n", runtime_address, instruction.text);
fmt::print("{:#x}" PRIX64 " {}\n", runtime_address, instruction.text); offset += instruction.info.length;
offset += instruction.info.length; runtime_address += instruction.info.length;
runtime_address += instruction.info.length; }
}
} }
void Linker::LoadDynamicInfo(Module* m) void Linker::LoadDynamicInfo(Module* m) {
{ for (const auto* dyn = reinterpret_cast<elf_dynamic*>(m->m_dynamic.data()); dyn->d_tag != DT_NULL; dyn++) {
for (const auto* dyn = reinterpret_cast<elf_dynamic*>(m->m_dynamic.data()); dyn->d_tag != DT_NULL; dyn++) switch (dyn->d_tag) {
{ case DT_SCE_HASH: // Offset of the hash table.
switch (dyn->d_tag) m->dynamic_info.hash_table = reinterpret_cast<void*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
{ break;
case DT_SCE_HASH: //Offset of the hash table. case DT_SCE_HASHSZ: // Size of the hash table
m->dynamic_info.hash_table = reinterpret_cast<void*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr); m->dynamic_info.hash_table_size = dyn->d_un.d_val;
break; break;
case DT_SCE_HASHSZ: //Size of the hash table case DT_SCE_STRTAB: // Offset of the string table.
m->dynamic_info.hash_table_size = dyn->d_un.d_val; m->dynamic_info.str_table = reinterpret_cast<char*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
break; break;
case DT_SCE_STRTAB://Offset of the string table. case DT_SCE_STRSZ: // Size of the string table.
m->dynamic_info.str_table = reinterpret_cast<char*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr); m->dynamic_info.str_table_size = dyn->d_un.d_val;
break; break;
case DT_SCE_STRSZ: //Size of the string table. case DT_SCE_SYMTAB: // Offset of the symbol table.
m->dynamic_info.str_table_size = dyn->d_un.d_val; m->dynamic_info.symbol_table = reinterpret_cast<elf_symbol*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
break; break;
case DT_SCE_SYMTAB://Offset of the symbol table. case DT_SCE_SYMTABSZ: // Size of the symbol table.
m->dynamic_info.symbol_table = reinterpret_cast<elf_symbol*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr); m->dynamic_info.symbol_table_total_size = dyn->d_un.d_val;
break; break;
case DT_SCE_SYMTABSZ://Size of the symbol table. case DT_INIT: m->dynamic_info.init_virtual_addr = dyn->d_un.d_ptr; break;
m->dynamic_info.symbol_table_total_size = dyn->d_un.d_val; case DT_FINI: m->dynamic_info.fini_virtual_addr = dyn->d_un.d_ptr; break;
break; case DT_SCE_PLTGOT: // Offset of the global offset table.
case DT_INIT: m->dynamic_info.pltgot_virtual_addr = dyn->d_un.d_ptr;
m->dynamic_info.init_virtual_addr = dyn->d_un.d_ptr; break;
break; case DT_SCE_JMPREL: // Offset of the table containing jump slots.
case DT_FINI: m->dynamic_info.jmp_relocation_table = reinterpret_cast<elf_relocation*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
m->dynamic_info.fini_virtual_addr = dyn->d_un.d_ptr; break;
break; case DT_SCE_PLTRELSZ: // Size of the global offset table.
case DT_SCE_PLTGOT: //Offset of the global offset table. m->dynamic_info.jmp_relocation_table_size = dyn->d_un.d_val;
m->dynamic_info.pltgot_virtual_addr = dyn->d_un.d_ptr; break;
break; case DT_SCE_PLTREL: // The type of relocations in the relocation table. Should be DT_RELA
case DT_SCE_JMPREL: //Offset of the table containing jump slots. m->dynamic_info.jmp_relocation_type = dyn->d_un.d_val;
m->dynamic_info.jmp_relocation_table = reinterpret_cast<elf_relocation*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr); if (m->dynamic_info.jmp_relocation_type != DT_RELA) {
break; LOG_WARN_IF(debug_loader, "DT_SCE_PLTREL is NOT DT_RELA should check!");
case DT_SCE_PLTRELSZ: //Size of the global offset table. }
m->dynamic_info.jmp_relocation_table_size = dyn->d_un.d_val; break;
break; case DT_SCE_RELA: // Offset of the relocation table.
case DT_SCE_PLTREL: //The type of relocations in the relocation table. Should be DT_RELA m->dynamic_info.relocation_table = reinterpret_cast<elf_relocation*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr);
m->dynamic_info.jmp_relocation_type = dyn->d_un.d_val; break;
if (m->dynamic_info.jmp_relocation_type != DT_RELA) case DT_SCE_RELASZ: // Size of the relocation table.
{ m->dynamic_info.relocation_table_size = dyn->d_un.d_val;
LOG_WARN_IF(debug_loader, "DT_SCE_PLTREL is NOT DT_RELA should check!"); break;
} case DT_SCE_RELAENT: // The size of relocation table entries.
break; m->dynamic_info.relocation_table_entries_size = dyn->d_un.d_val;
case DT_SCE_RELA: //Offset of the relocation table. if (m->dynamic_info.relocation_table_entries_size != 0x18) // this value should always be 0x18
m->dynamic_info.relocation_table = reinterpret_cast<elf_relocation*>(m->m_dynamic_data.data() + dyn->d_un.d_ptr); {
break; LOG_WARN_IF(debug_loader, "DT_SCE_RELAENT is NOT 0x18 should check!");
case DT_SCE_RELASZ: //Size of the relocation table. }
m->dynamic_info.relocation_table_size = dyn->d_un.d_val; break;
break; case DT_INIT_ARRAY: // Address of the array of pointers to initialization functions
case DT_SCE_RELAENT : //The size of relocation table entries. m->dynamic_info.init_array_virtual_addr = dyn->d_un.d_ptr;
m->dynamic_info.relocation_table_entries_size = dyn->d_un.d_val; break;
if (m->dynamic_info.relocation_table_entries_size != 0x18) //this value should always be 0x18 case DT_FINI_ARRAY: // Address of the array of pointers to termination functions
{ m->dynamic_info.fini_array_virtual_addr = dyn->d_un.d_ptr;
LOG_WARN_IF(debug_loader, "DT_SCE_RELAENT is NOT 0x18 should check!"); break;
} case DT_INIT_ARRAYSZ: // Size in bytes of the array of initialization functions
break; m->dynamic_info.init_array_size = dyn->d_un.d_val;
case DT_INIT_ARRAY:// Address of the array of pointers to initialization functions break;
m->dynamic_info.init_array_virtual_addr = dyn->d_un.d_ptr; case DT_FINI_ARRAYSZ: // Size in bytes of the array of terminationfunctions
break; m->dynamic_info.fini_array_size = dyn->d_un.d_val;
case DT_FINI_ARRAY: // Address of the array of pointers to termination functions break;
m->dynamic_info.fini_array_virtual_addr = dyn->d_un.d_ptr; case DT_PREINIT_ARRAY: // Address of the array of pointers to pre - initialization functions
break; m->dynamic_info.preinit_array_virtual_addr = dyn->d_un.d_ptr;
case DT_INIT_ARRAYSZ://Size in bytes of the array of initialization functions break;
m->dynamic_info.init_array_size = dyn->d_un.d_val; case DT_PREINIT_ARRAYSZ: // Size in bytes of the array of pre - initialization functions
break; m->dynamic_info.preinit_array_size = dyn->d_un.d_val;
case DT_FINI_ARRAYSZ://Size in bytes of the array of terminationfunctions break;
m->dynamic_info.fini_array_size = dyn->d_un.d_val; case DT_SCE_SYMENT: // The size of symbol table entries
break; m->dynamic_info.symbol_table_entries_size = dyn->d_un.d_val;
case DT_PREINIT_ARRAY://Address of the array of pointers to pre - initialization functions if (m->dynamic_info.symbol_table_entries_size != 0x18) // this value should always be 0x18
m->dynamic_info.preinit_array_virtual_addr = dyn->d_un.d_ptr; {
break; LOG_WARN_IF(debug_loader, "DT_SCE_SYMENT is NOT 0x18 should check!");
case DT_PREINIT_ARRAYSZ://Size in bytes of the array of pre - initialization functions }
m->dynamic_info.preinit_array_size = dyn->d_un.d_val; break;
break; case DT_DEBUG: m->dynamic_info.debug = dyn->d_un.d_val; break;
case DT_SCE_SYMENT: //The size of symbol table entries case DT_TEXTREL: m->dynamic_info.textrel = dyn->d_un.d_val; break;
m->dynamic_info.symbol_table_entries_size = dyn->d_un.d_val; case DT_FLAGS:
if (m->dynamic_info.symbol_table_entries_size != 0x18) //this value should always be 0x18 m->dynamic_info.flags = dyn->d_un.d_val;
{ if (m->dynamic_info.flags != 0x04) // this value should always be DF_TEXTREL (0x04)
LOG_WARN_IF(debug_loader, "DT_SCE_SYMENT is NOT 0x18 should check!"); {
} LOG_WARN_IF(debug_loader, "DT_FLAGS is NOT 0x04 should check!\n");
break; }
case DT_DEBUG: break;
m->dynamic_info.debug = dyn->d_un.d_val; case DT_NEEDED: // Offset of the library string in the string table to be linked in.
break; if (m->dynamic_info.str_table != nullptr) // in theory this should already be filled from about just make a test case
case DT_TEXTREL: {
m->dynamic_info.textrel = dyn->d_un.d_val; m->dynamic_info.needed.push_back(m->dynamic_info.str_table + dyn->d_un.d_val);
break; } else {
case DT_FLAGS: LOG_ERROR_IF(debug_loader, "DT_NEEDED str table is not loaded should check!");
m->dynamic_info.flags = dyn->d_un.d_val; }
if (m->dynamic_info.flags != 0x04) //this value should always be DF_TEXTREL (0x04) break;
{ case DT_SCE_NEEDED_MODULE: {
LOG_WARN_IF(debug_loader, "DT_FLAGS is NOT 0x04 should check!"); ModuleInfo info{};
} info.value = dyn->d_un.d_val;
break;
case DT_NEEDED://Offset of the library string in the string table to be linked in.
if (m->dynamic_info.str_table != nullptr)//in theory this should already be filled from about just make a test case
{
m->dynamic_info.needed.push_back(m->dynamic_info.str_table + dyn->d_un.d_val);
}
else
{
LOG_ERROR_IF(debug_loader, "DT_NEEDED str table is not loaded should check!");
}
break;
case DT_SCE_NEEDED_MODULE:
{
ModuleInfo info{};
info.value = dyn->d_un.d_val;
info.name = m->dynamic_info.str_table + info.name_offset; info.name = m->dynamic_info.str_table + info.name_offset;
info.enc_id = encodeId(info.id); info.enc_id = encodeId(info.id);
m->dynamic_info.import_modules.push_back(info); m->dynamic_info.import_modules.push_back(info);
} } break;
break; case DT_SCE_IMPORT_LIB: {
case DT_SCE_IMPORT_LIB: LibraryInfo info{};
{ info.value = dyn->d_un.d_val;
LibraryInfo info{};
info.value = dyn->d_un.d_val;
info.name = m->dynamic_info.str_table + info.name_offset; info.name = m->dynamic_info.str_table + info.name_offset;
info.enc_id = encodeId(info.id); info.enc_id = encodeId(info.id);
m->dynamic_info.import_libs.push_back(info); m->dynamic_info.import_libs.push_back(info);
} } break;
break; case DT_SCE_FINGERPRINT:
case DT_SCE_FINGERPRINT: // The fingerprint is a 24 byte (0x18) size buffer that contains a unique identifier for the given app.
//The fingerprint is a 24 byte (0x18) size buffer that contains a unique identifier for the given app. // How exactly this is generated isn't known, however it is not necessary to have a valid fingerprint.
//How exactly this is generated isn't known, however it is not necessary to have a valid fingerprint. // While an invalid fingerprint will cause a warning to be printed to the kernel log, the ELF will still load and run.
//While an invalid fingerprint will cause a warning to be printed to the kernel log, the ELF will still load and run. LOG_INFO_IF(debug_loader, "unsupported DT_SCE_FINGERPRINT value = ..........: {:#018x}\n", dyn->d_un.d_val);
LOG_INFO_IF(debug_loader, "unsupported DT_SCE_FINGERPRINT value = ..........: {:#018x}\n", dyn->d_un.d_val); break;
break; case DT_SCE_IMPORT_LIB_ATTR:
case DT_SCE_IMPORT_LIB_ATTR: // The upper 32-bits should contain the module index multiplied by 0x10000. The lower 32-bits should be a constant 0x9.
//The upper 32-bits should contain the module index multiplied by 0x10000. The lower 32-bits should be a constant 0x9. LOG_INFO_IF(debug_loader, "unsupported DT_SCE_IMPORT_LIB_ATTR value = ..........: {:#018x}\n", dyn->d_un.d_val);
LOG_INFO_IF(debug_loader, "unsupported DT_SCE_IMPORT_LIB_ATTR value = ..........: {:#018x}\n", dyn->d_un.d_val); break;
break; case DT_SCE_ORIGINAL_FILENAME: m->dynamic_info.filename = m->dynamic_info.str_table + dyn->d_un.d_val; break;
case DT_SCE_ORIGINAL_FILENAME: case DT_SCE_MODULE_INFO: // probably only useable in shared modules
m->dynamic_info.filename = m->dynamic_info.str_table + dyn->d_un.d_val; {
break; ModuleInfo info{};
case DT_SCE_MODULE_INFO://probably only useable in shared modules info.value = dyn->d_un.d_val;
{
ModuleInfo info{};
info.value = dyn->d_un.d_val;
info.name = m->dynamic_info.str_table + info.name_offset; info.name = m->dynamic_info.str_table + info.name_offset;
info.enc_id = encodeId(info.id); info.enc_id = encodeId(info.id);
m->dynamic_info.export_modules.push_back(info); m->dynamic_info.export_modules.push_back(info);
} } break;
break; case DT_SCE_MODULE_ATTR:
case DT_SCE_MODULE_ATTR: // TODO?
//TODO? LOG_INFO_IF(debug_loader, "unsupported DT_SCE_MODULE_ATTR value = ..........: {:#018x}\n", dyn->d_un.d_val);
LOG_INFO_IF(debug_loader, "unsupported DT_SCE_MODULE_ATTR value = ..........: {:#018x}\n", dyn->d_un.d_val); break;
break; case DT_SCE_EXPORT_LIB: {
case DT_SCE_EXPORT_LIB: LibraryInfo info{};
{ info.value = dyn->d_un.d_val;
LibraryInfo info{};
info.value = dyn->d_un.d_val;
info.name = m->dynamic_info.str_table + info.name_offset; info.name = m->dynamic_info.str_table + info.name_offset;
info.enc_id = encodeId(info.id); info.enc_id = encodeId(info.id);
m->dynamic_info.export_libs.push_back(info); m->dynamic_info.export_libs.push_back(info);
} } break;
break; default: LOG_INFO_IF(debug_loader, "unsupported dynamic tag ..........: {:#018x}\n", dyn->d_tag);
default: }
LOG_INFO_IF(debug_loader, "unsupported dynamic tag ..........: {:#018x}\n", dyn->d_tag); }
}
}
} }
const ModuleInfo* Linker::FindModule(const Module& m, const std::string& id) const ModuleInfo* Linker::FindModule(const Module& m, const std::string& id) {
{
const auto& import_modules = m.dynamic_info.import_modules; const auto& import_modules = m.dynamic_info.import_modules;
int index = 0; int index = 0;
for (const auto& mod : import_modules) for (const auto& mod : import_modules) {
{ if (mod.enc_id.compare(id) == 0) {
if (mod.enc_id.compare(id) == 0) return &import_modules.at(index);
{ }
return &import_modules.at(index); index++;
} }
index++;
}
const auto& export_modules = m.dynamic_info.export_modules; const auto& export_modules = m.dynamic_info.export_modules;
index = 0; index = 0;
for (const auto& mod : export_modules) for (const auto& mod : export_modules) {
{ if (mod.enc_id.compare(id) == 0) {
if (mod.enc_id.compare(id) == 0) return &export_modules.at(index);
{ }
return &export_modules.at(index); index++;
} }
index++; return nullptr;
}
return nullptr;
} }
const LibraryInfo* Linker::FindLibrary(const Module& m, const std::string& id) const LibraryInfo* Linker::FindLibrary(const Module& m, const std::string& id) {
{
const auto& import_libs = m.dynamic_info.import_libs; const auto& import_libs = m.dynamic_info.import_libs;
int index = 0; int index = 0;
for (const auto& lib : import_libs) for (const auto& lib : import_libs) {
{ if (lib.enc_id.compare(id) == 0) {
if (lib.enc_id.compare(id) == 0) return &import_libs.at(index);
{ }
return &import_libs.at(index); index++;
} }
index++;
}
const auto& export_libs = m.dynamic_info.export_libs; const auto& export_libs = m.dynamic_info.export_libs;
index = 0; index = 0;
for (const auto& lib : export_libs) for (const auto& lib : export_libs) {
{ if (lib.enc_id.compare(id) == 0) {
if (lib.enc_id.compare(id) == 0) return &export_libs.at(index);
{ }
return &export_libs.at(index); index++;
} }
index++; return nullptr;
}
return nullptr;
} }
void Linker::LoadSymbols(Module* m) void Linker::LoadSymbols(Module* m) {
{ if (m->dynamic_info.symbol_table == nullptr || m->dynamic_info.str_table == nullptr || m->dynamic_info.symbol_table_total_size == 0) {
if (m->dynamic_info.symbol_table == nullptr || m->dynamic_info.str_table == nullptr || m->dynamic_info.symbol_table_total_size==0) LOG_INFO_IF(debug_loader, "Symbol table not found!\n");
{ return;
LOG_INFO_IF(debug_loader, "Symbol table not found!\n"); }
return;
}
for (auto* sym = m->dynamic_info.symbol_table; for (auto* sym = m->dynamic_info.symbol_table;
reinterpret_cast<uint8_t*>(sym) < reinterpret_cast<uint8_t*>(m->dynamic_info.symbol_table) + m->dynamic_info.symbol_table_total_size; reinterpret_cast<uint8_t*>(sym) < reinterpret_cast<uint8_t*>(m->dynamic_info.symbol_table) + m->dynamic_info.symbol_table_total_size;
sym++) sym++) {
{
std::string id = std::string(m->dynamic_info.str_table + sym->st_name); std::string id = std::string(m->dynamic_info.str_table + sym->st_name);
const auto ids = Common::SplitString(id, '#'); const auto ids = Common::SplitString(id, '#');
if (ids.size() == 3)//symbols are 3 parts name , library , module if (ids.size() == 3) // symbols are 3 parts name , library , module
{ {
const auto* library = FindLibrary(*m, ids.at(1)); const auto* library = FindLibrary(*m, ids.at(1));
const auto* module = FindModule(*m, ids.at(2)); const auto* module = FindModule(*m, ids.at(2));
auto bind = sym->GetBind(); auto bind = sym->GetBind();
auto type = sym->GetType(); auto type = sym->GetType();
auto visibility = sym->GetVisibility(); auto visibility = sym->GetVisibility();
if (library != nullptr || module != nullptr) if (library != nullptr || module != nullptr) {
{ switch (bind) {
switch (bind) case STB_GLOBAL:
{ case STB_WEAK: break;
case STB_GLOBAL: default: LOG_INFO_IF(debug_loader, "Unsupported bind {} for name symbol {} \n", bind, ids.at(0)); break;
case STB_WEAK: }
break; switch (type) {
default: case STT_OBJECT:
LOG_INFO_IF(debug_loader, "Unsupported bind {} for name symbol {} \n", bind,ids.at(0)); case STT_FUN: break;
continue; default: LOG_INFO_IF(debug_loader, "Unsupported type {} for name symbol {} \n", type, ids.at(0)); break;
} }
switch (type) switch (visibility) {
{ case STV_DEFAULT: break;
case STT_OBJECT: default: LOG_INFO_IF(debug_loader, "Unsupported visibility {} for name symbol {} \n", visibility, ids.at(0)); break;
case STT_FUN: }
break; // if st_value!=0 then it's export symbol
default: bool is_sym_export = sym->st_value != 0;
LOG_INFO_IF(debug_loader, "Unsupported type {} for name symbol {} \n", type, ids.at(0)); std::string nidName = "";
continue;
}
switch (visibility)
{
case STV_DEFAULT:
break;
default:
LOG_INFO_IF(debug_loader, "Unsupported visibility {} for name symbol {} \n", visibility, ids.at(0));
continue;
}
//if st_value!=0 then it's export symbol
bool is_sym_export = sym->st_value != 0;
std::string nidName = "";
auto aeronid = AeroLib::FindByNid(ids.at(0).c_str()); auto aeronid = AeroLib::FindByNid(ids.at(0).c_str());
if (aeronid != nullptr) if (aeronid != nullptr) {
{
nidName = aeronid->name; nidName = aeronid->name;
} } else {
else nidName = "UNK";
{ }
nidName = "UNK";
}
Loader::SymbolRes sym_r{}; Loader::SymbolRes sym_r{};
sym_r.name = ids.at(0); sym_r.name = ids.at(0);
sym_r.nidName = nidName; sym_r.nidName = nidName;
sym_r.library = library->name; sym_r.library = library->name;
sym_r.library_version = library->version; sym_r.library_version = library->version;
sym_r.module = module->name; sym_r.module = module->name;
sym_r.module_version_major = module->version_major; sym_r.module_version_major = module->version_major;
sym_r.module_version_minor = module->version_minor; sym_r.module_version_minor = module->version_minor;
sym_r.type = type; sym_r.type = type;
if (is_sym_export) if (is_sym_export) {
{
m->export_sym.AddSymbol(sym_r, sym->st_value + m->base_virtual_addr); m->export_sym.AddSymbol(sym_r, sym->st_value + m->base_virtual_addr);
} } else {
else m->import_sym.AddSymbol(sym_r, 0);
{ }
m->import_sym.AddSymbol(sym_r,0);
}
LOG_INFO_IF(debug_loader, "name {} function {} library {} module {} bind {} type {} visibility {}\n", ids.at(0),nidName,library->name, module->name, bind, type, visibility); LOG_INFO_IF(debug_loader, "name {} function {} library {} module {} bind {} type {} visibility {}\n", ids.at(0), nidName,
} library->name, module->name, bind, type, visibility);
} }
} }
}
} }
static void relocate(u32 idx, elf_relocation* rel, Module* m, bool isJmpRel) { static void relocate(u32 idx, elf_relocation* rel, Module* m, bool isJmpRel) {
auto type = rel->GetType(); auto type = rel->GetType();
@ -501,6 +411,7 @@ static void relocate(u32 idx, elf_relocation* rel, Module* m, bool isJmpRel) {
u64 rel_virtual_addr = m->base_virtual_addr + rel->rel_offset; u64 rel_virtual_addr = m->base_virtual_addr + rel->rel_offset;
bool rel_isResolved = false; bool rel_isResolved = false;
u08 rel_sym_type = 0; u08 rel_sym_type = 0;
u08 rel_sym_bind = 0;
std::string rel_name; std::string rel_name;
switch (type) { switch (type) {
@ -546,34 +457,53 @@ static void relocate(u32 idx, elf_relocation* rel, Module* m, bool isJmpRel) {
LOG_INFO_IF(debug_loader, "R_X86_64_64-R_X86_64_JUMP_SLOT sym_type {} bind STB_GLOBAL symbol : {:#010x}\n", sym_type, symbol); LOG_INFO_IF(debug_loader, "R_X86_64_64-R_X86_64_JUMP_SLOT sym_type {} bind STB_GLOBAL symbol : {:#010x}\n", sym_type, symbol);
} }
break; break;
case STB_WEAK:
rel_name = namesTlb + sym.st_name;
m->linker->Resolve(rel_name, rel_sym_type, m, &symrec);
symbol_vitrual_addr = symrec.virtual_address;
rel_isResolved = (symbol_vitrual_addr != 0);
rel_name = symrec.name;
if (type == R_X86_64_JUMP_SLOT) {
addend = 0;
}
rel_value = (rel_isResolved ? symbol_vitrual_addr + addend : 0);
if (!rel_isResolved) {
LOG_INFO_IF(debug_loader, "R_X86_64_64-R_X86_64_JUMP_SLOT sym_type {} bind STB_WEAK symbol : {:#010x}\n", sym_type, symbol);
}
break;
default: LOG_INFO_IF(debug_loader, "UNK bind {}\n", sym_bind); default: LOG_INFO_IF(debug_loader, "UNK bind {}\n", sym_bind);
} }
} break; } break;
case R_X86_64_DTPMOD64:
rel_value = reinterpret_cast<u64>(m);
rel_isResolved = true;
rel_sym_type = STT_TLS;
rel_sym_bind = STB_LOCAL;
break;
default: LOG_INFO_IF(debug_loader, "UNK type {:#010x} rel symbol : {:#010x}\n", type, symbol); default: LOG_INFO_IF(debug_loader, "UNK type {:#010x} rel symbol : {:#010x}\n", type, symbol);
} }
if (rel_isResolved) { if (rel_isResolved) {
VirtualMemory::memory_patch(rel_virtual_addr, rel_value); VirtualMemory::memory_patch(rel_virtual_addr, rel_value);
} } else {
else LOG_INFO_IF(debug_loader, "function not patched! {}\n", rel_name);
{ }
LOG_INFO_IF(debug_loader, "function not patched! {}\n",rel_name);
}
} }
void Linker::Relocate(Module* m) void Linker::Relocate(Module* m) {
{ u32 idx = 0;
u32 idx = 0; for (auto* rel = m->dynamic_info.relocation_table;
for (auto* rel = m->dynamic_info.relocation_table; reinterpret_cast<u08*>(rel) < reinterpret_cast<u08*>(m->dynamic_info.relocation_table) + m->dynamic_info.relocation_table_size; rel++, idx++) reinterpret_cast<u08*>(rel) < reinterpret_cast<u08*>(m->dynamic_info.relocation_table) + m->dynamic_info.relocation_table_size;
{ rel++, idx++) {
relocate(idx, rel, m, false); relocate(idx, rel, m, false);
} }
idx = 0; idx = 0;
for (auto* rel = m->dynamic_info.jmp_relocation_table; reinterpret_cast<u08*>(rel) < reinterpret_cast<u08*>(m->dynamic_info.jmp_relocation_table) + m->dynamic_info.jmp_relocation_table_size; rel++, idx++) for (auto* rel = m->dynamic_info.jmp_relocation_table;
{ reinterpret_cast<u08*>(rel) < reinterpret_cast<u08*>(m->dynamic_info.jmp_relocation_table) + m->dynamic_info.jmp_relocation_table_size;
relocate(idx, rel, m, true); rel++, idx++) {
} relocate(idx, rel, m, true);
}
} }
template <typename T> template <typename T>
@ -583,13 +513,13 @@ bool contains(const std::vector<T>& vecObj, const T& element) {
} }
Module* Linker::FindModuleExp(const ModuleInfo& module, const LibraryInfo& library) { Module* Linker::FindModuleExp(const ModuleInfo& module, const LibraryInfo& library) {
//std::scoped_lock lock{m_mutex}; // std::scoped_lock lock{m_mutex};
for (auto& m : m_modules) { for (auto& m : m_modules) {
const auto& export_libs = m.dynamic_info.export_libs; const auto& export_libs = m.dynamic_info.export_libs;
const auto& export_modules = m.dynamic_info.export_modules; const auto& export_modules = m.dynamic_info.export_modules;
if (contains(export_libs, library) && contains(export_modules,module)) { if (contains(export_libs, library) && contains(export_modules, module)) {
return &m; return &m;
} }
} }
@ -597,14 +527,14 @@ Module* Linker::FindModuleExp(const ModuleInfo& module, const LibraryInfo& libra
} }
void Linker::Resolve(const std::string& name, int Symtype, Module* m, Loader::SymbolRecord* return_info) { void Linker::Resolve(const std::string& name, int Symtype, Module* m, Loader::SymbolRecord* return_info) {
//std::scoped_lock lock{m_mutex}; // std::scoped_lock lock{m_mutex};
const auto ids = Common::SplitString(name, '#'); const auto ids = Common::SplitString(name, '#');
if (ids.size() == 3) // symbols are 3 parts name , library , module if (ids.size() == 3) // symbols are 3 parts name , library , module
{ {
const auto* library = FindLibrary(*m, ids.at(1)); const auto* library = FindLibrary(*m, ids.at(1));
const auto* module = FindModule(*m, ids.at(2)); const auto* module = FindModule(*m, ids.at(2));
if (library != nullptr && module != nullptr) { if (library != nullptr && module != nullptr) {
Loader::SymbolRes sr{}; Loader::SymbolRes sr{};
sr.name = ids.at(0); sr.name = ids.at(0);
sr.library = library->name; sr.library = library->name;
@ -617,10 +547,9 @@ void Linker::Resolve(const std::string& name, int Symtype, Module* m, Loader::Sy
const Loader::SymbolRecord* rec = nullptr; const Loader::SymbolRecord* rec = nullptr;
rec = m_hle_symbols.FindSymbol(sr); rec = m_hle_symbols.FindSymbol(sr);
if (rec == nullptr) { if (rec == nullptr) {
if (auto* p = FindModuleExp(*module, *library); p != nullptr && p->export_sym.GetSize()>0) { if (auto* p = FindModuleExp(*module, *library); p != nullptr && p->export_sym.GetSize() > 0) {
rec = p->export_sym.FindSymbol(sr); rec = p->export_sym.FindSymbol(sr);
} }
} }
if (rec != nullptr) { if (rec != nullptr) {
@ -633,64 +562,57 @@ void Linker::Resolve(const std::string& name, int Symtype, Module* m, Loader::Sy
} else { } else {
return_info->virtual_address = AeroLib::GetStub(sr.name.c_str()); return_info->virtual_address = AeroLib::GetStub(sr.name.c_str());
return_info->name = "Unknown !!!"; return_info->name = "Unknown !!!";
} }
LOG_ERROR_IF(debug_loader, "Linker: Stub resolved {} as {} (lib: {}, mod: {}) \n", sr.name, return_info->name, library->name, module->name); LOG_ERROR_IF(debug_loader, "Linker: Stub resolved {} as {} (lib: {}, mod: {}) \n", sr.name, return_info->name, library->name,
module->name);
} }
} } else {
else __debugbreak(); // den tha prepei na ftasoume edo
{ }
__debugbreak();//den tha prepei na ftasoume edo } else {
} __debugbreak(); // oute edo mallon
} }
else
{
__debugbreak();//oute edo mallon
}
} }
using exit_func_t = PS4_SYSV_ABI void (*)(); using exit_func_t = PS4_SYSV_ABI void (*)();
using entry_func_t = PS4_SYSV_ABI void (*)(EntryParams* params, exit_func_t atexit_func); using entry_func_t = PS4_SYSV_ABI void (*)(EntryParams* params, exit_func_t atexit_func);
static PS4_SYSV_ABI void ProgramExitFunc() { static PS4_SYSV_ABI void ProgramExitFunc() { fmt::print("exit function called\n"); }
fmt::print("exit function called\n");
}
static void run_main_entry(u64 addr, EntryParams* params, exit_func_t exit_func) { static void run_main_entry(u64 addr, EntryParams* params, exit_func_t exit_func) {
//reinterpret_cast<entry_func_t>(addr)(params, exit_func); // can't be used, stack has to have a specific layout // reinterpret_cast<entry_func_t>(addr)(params, exit_func); // can't be used, stack has to have a specific layout
asm volatile ( asm volatile(
"andq $-16, %%rsp\n"// Align to 16 bytes "andq $-16, %%rsp\n" // Align to 16 bytes
"subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned "subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned
// Kernel also pushes some more things here during process init // Kernel also pushes some more things here during process init
// at least: environment, auxv, possibly other things // at least: environment, auxv, possibly other things
"pushq 8(%1)\n" // copy EntryParams to top of stack like the kernel does "pushq 8(%1)\n" // copy EntryParams to top of stack like the kernel does
"pushq 0(%1)\n" // OpenOrbis expects to find it there "pushq 0(%1)\n" // OpenOrbis expects to find it there
"movq %1, %%rdi\n" // also pass params and exit func "movq %1, %%rdi\n" // also pass params and exit func
"movq %2, %%rsi\n" // as before "movq %2, %%rsi\n" // as before
"jmp *%0\n" // can't use call here, as that would mangle the prepared stack. "jmp *%0\n" // can't use call here, as that would mangle the prepared stack.
// there's no coming back // there's no coming back
: :
: "r"(addr), "r"(params), "r"(exit_func) : "r"(addr), "r"(params), "r"(exit_func)
: "rax", "rsi", "rdi", "rsp", "rbp" : "rax", "rsi", "rdi", "rsp", "rbp");
);
} }
void Linker::Execute() { void Linker::Execute() {
Core::Libraries::LibKernel::pthreadInitSelfMainThread(); Core::Libraries::LibKernel::pthreadInitSelfMainThread();
EntryParams p{}; EntryParams p{};
p.argc = 1; p.argc = 1;
p.argv[0] = "eboot.bin"; //hmm should be ok? p.argv[0] = "eboot.bin"; // hmm should be ok?
for (auto& m : m_modules) { for (auto& m : m_modules) {
if (!m.elf.IsSharedLib()) { if (!m.elf.IsSharedLib()) {
run_main_entry(m.elf.GetElfEntry() + m.base_virtual_addr, &p, ProgramExitFunc); run_main_entry(m.elf.GetElfEntry() + m.base_virtual_addr, &p, ProgramExitFunc);
} }
} }
} }
} // namespace Core } // namespace Core

View File

@ -115,7 +115,6 @@ public:
virtual ~Linker(); virtual ~Linker();
Module* LoadModule(const std::string& elf_name); Module* LoadModule(const std::string& elf_name);
Module* FindModule(/*u32 id*/);
void LoadModuleToMemory(Module* m); void LoadModuleToMemory(Module* m);
void LoadDynamicInfo(Module* m); void LoadDynamicInfo(Module* m);
void LoadSymbols(Module* m); void LoadSymbols(Module* m);

View File

@ -449,6 +449,7 @@ struct elf_relocation
constexpr u32 R_X86_64_64 = 1; // Direct 64 bit constexpr u32 R_X86_64_64 = 1; // Direct 64 bit
constexpr u32 R_X86_64_JUMP_SLOT = 7; // Create PLT entry constexpr u32 R_X86_64_JUMP_SLOT = 7; // Create PLT entry
constexpr u32 R_X86_64_RELATIVE = 8; // Adjust by program base constexpr u32 R_X86_64_RELATIVE = 8; // Adjust by program base
constexpr u32 R_X86_64_DTPMOD64 = 16;
namespace Core::Loader { namespace Core::Loader {