PPU LLVM: relocation support

This commit is contained in:
Nekotekina 2017-07-01 02:08:51 +03:00
parent 9fccb47e7c
commit f0d184f38b
7 changed files with 666 additions and 237 deletions

View file

@ -112,7 +112,7 @@ struct ppu_linkage_info
bool hle = false;
u32 export_addr = 0;
std::set<u32> imports;
std::set<u32> weak_imports;
std::set<u32> frefss;
};
// FNID -> (export; [imports...])
@ -342,9 +342,8 @@ static void ppu_initialize_modules(const std::shared_ptr<ppu_linkage_info>& link
}
}
// Link variables/weak imports, looks to be literally the same as vrefs
// Don't use bool flag in VREFs, originally it didn't have it
static void ppu_patch_refs(u32 fref, u32 faddr)
// Resolve relocations for variable/function linkage.
static void ppu_patch_refs(std::vector<ppu_reloc>* out_relocs, u32 fref, u32 faddr)
{
struct ref_t
{
@ -357,42 +356,52 @@ static void ppu_patch_refs(u32 fref, u32 faddr)
{
if (ref->addend) LOG_WARNING(LOADER, "**** REF(%u): Addend value(0x%x, 0x%x)", ref->type, ref->addr, ref->addend);
const auto ref_import = vm::ptr<u32>::make(faddr + ref->addend);
const u32 raddr = ref->addr;
const u32 rtype = ref->type;
const u32 rdata = faddr + ref->addend;
const auto addr = faddr + ref->addend;
if (out_relocs)
{
// Register relocation with unpredictable target (data=0)
ppu_reloc _rel;
_rel.addr = raddr;
_rel.type = rtype;
_rel.data = 0;
out_relocs->emplace_back(_rel);
}
// OPs are probably similar to relocations
switch (u32 type = ref->type)
// OPs must be similar to relocations
switch (rtype)
{
case 0x1:
case 1:
{
const u32 value = vm::_ref<u32>(ref->addr) = addr;
const u32 value = vm::_ref<u32>(ref->addr) = rdata;
LOG_TRACE(LOADER, "**** REF(1): 0x%x <- 0x%x", ref->addr, value);
break;
}
case 0x4:
case 4:
{
const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(addr);
const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(rdata);
LOG_TRACE(LOADER, "**** REF(4): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr);
break;
}
case 0x6:
case 6:
{
const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(addr >> 16) + (addr & 0x8000 ? 1 : 0);
const u16 value = vm::_ref<u16>(ref->addr) = static_cast<u16>(rdata >> 16) + (rdata & 0x8000 ? 1 : 0);
LOG_TRACE(LOADER, "**** REF(6): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr);
break;
}
case 57:
{
const u16 value = vm::_ref<ppu_bf_t<be_t<u16>, 0, 14>>(ref->addr) = static_cast<u16>(addr) >> 2;
const u16 value = vm::_ref<ppu_bf_t<be_t<u16>, 0, 14>>(ref->addr) = static_cast<u16>(rdata) >> 2;
LOG_TRACE(LOADER, "**** REF(57): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr);
break;
}
default: LOG_ERROR(LOADER, "**** REF(%u): Unknown/Illegal type (0x%x, 0x%x)", ref->type, ref->addr, ref->addend);
default: LOG_ERROR(LOADER, "**** REF(%u): Unknown/Illegal type (0x%x, 0x%x)", rtype, raddr, ref->addend);
}
}
}
@ -523,9 +532,9 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
//LOG_WARNING(LOADER, "Exported function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
}
for (const u32 weak_import_addr : flink.weak_imports)
for (const u32 fref : flink.frefss)
{
ppu_patch_refs(weak_import_addr, faddr);
ppu_patch_refs(nullptr, fref, faddr);
}
}
}
@ -557,7 +566,7 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
// Fix imports
for (const auto vref : vlink.imports)
{
ppu_patch_refs(vref, vaddr);
ppu_patch_refs(nullptr, vref, vaddr);
//LOG_WARNING(LOADER, "Exported variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
}
}
@ -569,7 +578,7 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
return result;
}
static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32 imports_start, u32 imports_end)
static void ppu_load_imports(std::vector<ppu_reloc>& relocs, const std::shared_ptr<ppu_linkage_info>& link, u32 imports_start, u32 imports_end)
{
for (u32 addr = imports_start; addr < imports_end;)
{
@ -577,7 +586,7 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
const std::string module_name(lib.name.get_ptr());
LOG_NOTICE(LOADER, "** Imported module '%s' (0x%x, 0x%x)", module_name, lib.unk4, lib.unk5);
LOG_NOTICE(LOADER, "** Imported module '%s' (ver=0x%x, attr=0x%x, 0x%x, 0x%x) [0x%x]", module_name, lib.version, lib.attributes, lib.unk4, lib.unk5, addr);
if (lib.num_tlsvar)
{
@ -607,21 +616,17 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
flink.imports.emplace(faddr);
mlink.imported = true;
// Link if available
if (flink.export_addr)
{
vm::write32(faddr, flink.export_addr);
}
else
{
vm::write32(faddr, ppu_function_manager::addr);
}
// Link address (special HLE function by default)
const u32 link_addr = flink.export_addr ? flink.export_addr : ppu_function_manager::addr;
//weak imports, possibly
if (((lib.attributes & 0x2000) == 0x2000) && fnids[i + lib.num_func] != 0) //0x2000 seems to be correct flag
// Write import table
vm::write32(faddr, link_addr);
// Patch refs if necessary (0x2000 seems to be correct flag indicating the presence of additional info)
if (const u32 frefs = (lib.attributes & 0x2000) ? +fnids[i + lib.num_func] : 0)
{
flink.weak_imports.emplace(fnids[i + lib.num_func]);
ppu_patch_refs(fnids[i + lib.num_func], flink.export_addr);
flink.frefss.emplace(frefs);
ppu_patch_refs(&relocs, frefs, link_addr);
}
//LOG_WARNING(LOADER, "Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr);
@ -644,10 +649,7 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
mlink.imported = true;
// Link if available
if (vlink.export_addr)
{
ppu_patch_refs(vref, vlink.export_addr);
}
ppu_patch_refs(&relocs, vref, vlink.export_addr);
//LOG_WARNING(LOADER, "Imported variable '%s' in module '%s' (0x%x)", ppu_get_variable_name(module_name, vnid), module_name, vlink.first);
}
@ -656,10 +658,13 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
}
}
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& name)
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path)
{
std::vector<std::pair<u32, u32>> segments;
std::vector<std::pair<u32, u32>> sections;
// Create new PRX object
const auto prx = idm::make_ptr<lv2_obj, lv2_prx>();
// Access linkage information object
const auto link = fxm::get_always<ppu_linkage_info>();
for (const auto& prog : elf.progs)
{
@ -693,7 +698,12 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
ppu_register_range(addr, mem_size);
}
segments.emplace_back(std::make_pair(addr, mem_size));
ppu_segment _seg;
_seg.addr = addr;
_seg.size = mem_size;
_seg.type = p_type;
_seg.flags = prog.p_flags;
prx->segs.emplace_back(_seg);
}
break;
@ -714,13 +724,18 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
if (s.sh_type == 1 && addr && size) // TODO: some sections with addr=0 are valid
{
for (auto i = 0; i < segments.size(); i++)
for (auto i = 0; i < prx->segs.size(); i++)
{
const u32 saddr = static_cast<u32>(elf.progs[i].p_vaddr);
if (addr >= saddr && addr < saddr + elf.progs[i].p_memsz)
{
// "Relocate" section
sections.emplace_back(std::make_pair(addr - saddr + segments[i].first, size));
ppu_segment _sec;
_sec.addr = addr - saddr + prx->segs[i].addr;
_sec.size = size;
_sec.type = s.sh_type;
_sec.flags = s.sh_flags & 7;
prx->secs.emplace_back(_sec);
break;
}
}
@ -749,10 +764,13 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
{
const auto& rel = reinterpret_cast<const ppu_prx_relocation_info&>(prog.bin[i]);
const u32 raddr = vm::cast(segments.at(rel.index_addr).first + rel.offset, HERE);
const u64 rdata = segments.at(rel.index_value).first + rel.ptr.addr();
ppu_reloc _rel;
const u32 raddr = _rel.addr = vm::cast(prx->segs.at(rel.index_addr).addr + rel.offset, HERE);
const u32 rtype = _rel.type = rel.type;
const u64 rdata = _rel.data = prx->segs.at(rel.index_value).addr + rel.ptr.addr();
prx->relocs.emplace_back(_rel);
switch (const u32 type = rel.type)
switch (rtype)
{
case 1: // R_PPC64_ADDR32
{
@ -817,7 +835,12 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
break;
}
default: LOG_ERROR(LOADER, "**** RELOCATION(%u): Illegal/Unknown type! (addr=0x%x; 0x%llx)", type, raddr, rdata);
default: LOG_ERROR(LOADER, "**** RELOCATION(%u): Illegal/Unknown type! (addr=0x%x; 0x%llx)", rtype, raddr, rdata);
}
if (rdata == 0)
{
LOG_TODO(LOADER, "**** RELOCATION(%u): 0x%x <- (zero-based value)", rtype, raddr);
}
}
@ -826,12 +849,6 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
}
}
// Access linkage information object
const auto link = fxm::get_always<ppu_linkage_info>();
// Create new PRX object
auto prx = idm::make_ptr<lv2_obj, lv2_prx>();
if (!elf.progs.empty() && elf.progs[0].p_paddr)
{
struct ppu_prx_library_info
@ -847,16 +864,16 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
};
// Access library information (TODO)
const auto& lib_info = vm::cptr<ppu_prx_library_info>(vm::cast(segments[0].first + elf.progs[0].p_paddr - elf.progs[0].p_offset, HERE));
const auto& lib_info = vm::cptr<ppu_prx_library_info>(vm::cast(prx->segs[0].addr + elf.progs[0].p_paddr - elf.progs[0].p_offset, HERE));
const auto& lib_name = std::string(lib_info->name);
LOG_WARNING(LOADER, "Library %s (rtoc=0x%x):", lib_name, lib_info->toc);
prx->specials = ppu_load_exports(link, lib_info->exports_start, lib_info->exports_end);
ppu_load_imports(link, lib_info->imports_start, lib_info->imports_end);
ppu_load_imports(prx->relocs, link, lib_info->imports_start, lib_info->imports_end);
prx->funcs = ppu_analyse(segments, sections, lib_info->toc, 0);
prx->analyse(lib_info->toc, 0);
}
else
{
@ -868,7 +885,8 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
prx->exit.set(prx->specials[0x3ab9a95e]);
prx->prologue.set(prx->specials[0x0D10FD3F]);
prx->epilogue.set(prx->specials[0x330F7005]);
prx->name = name;
prx->name = path.substr(path.find_last_of('/') + 1);
prx->path = path;
return prx;
}
@ -879,15 +897,12 @@ void ppu_load_exec(const ppu_exec_object& elf)
LOG_TODO(LOADER, "'Hook static functions' option deactivated");
}
// Set for delayed initialization in ppu_initialize()
const auto _main = fxm::make<ppu_module>();
// Access linkage information object
const auto link = fxm::get_always<ppu_linkage_info>();
// Segment info
std::vector<std::pair<u32, u32>> segments;
// Section info (optional)
std::vector<std::pair<u32, u32>> sections;
// TLS information
u32 tls_vaddr = 0;
u32 tls_fsize = 0;
@ -904,10 +919,13 @@ void ppu_load_exec(const ppu_exec_object& elf)
{
LOG_NOTICE(LOADER, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
const u32 addr = vm::cast(prog.p_vaddr, HERE);
const u32 size = ::narrow<u32>(prog.p_memsz, "p_memsz" HERE);
if (prog.p_type == 0x1 /* LOAD */ && prog.p_memsz)
ppu_segment _seg;
const u32 addr = _seg.addr = vm::cast(prog.p_vaddr, HERE);
const u32 size = _seg.size = ::narrow<u32>(prog.p_memsz, "p_memsz" HERE);
const u32 type = _seg.type = prog.p_type;
const u32 flag = _seg.flags = prog.p_flags;
if (type == 0x1 /* LOAD */ && prog.p_memsz)
{
if (prog.bin.size() > size || prog.bin.size() != prog.p_filesz)
fmt::throw_exception("Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size);
@ -924,7 +942,8 @@ void ppu_load_exec(const ppu_exec_object& elf)
ppu_register_range(addr, size);
}
segments.emplace_back(std::make_pair(addr, size));
// Store only LOAD segments (TODO)
_main->segs.emplace_back(_seg);
}
}
@ -933,12 +952,15 @@ void ppu_load_exec(const ppu_exec_object& elf)
{
LOG_NOTICE(LOADER, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", s.sh_type, s.sh_addr, s.sh_size, s.sh_flags);
const u32 addr = vm::cast(s.sh_addr);
const u32 size = vm::cast(s.sh_size);
ppu_segment _sec;
const u32 addr = _sec.addr = vm::cast(s.sh_addr);
const u32 size = _sec.size = vm::cast(s.sh_size);
const u32 type = _sec.type = s.sh_type;
const u32 flag = _sec.flags = s.sh_flags & 7;
if (s.sh_type == 1 && addr && size)
{
sections.emplace_back(std::make_pair(addr, size));
_main->secs.emplace_back(_sec);
}
}
@ -1038,7 +1060,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
ppu_load_exports(link, proc_prx_param.libent_start, proc_prx_param.libent_end);
ppu_load_imports(link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
ppu_load_imports(_main->relocs, link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
}
break;
}
@ -1209,7 +1231,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
{
LOG_WARNING(LOADER, "Loading library: %s", name);
auto prx = ppu_load_prx(obj, name);
auto prx = ppu_load_prx(obj, lle_dir + name);
if (prx->funcs.empty())
{
@ -1218,7 +1240,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
else
{
// TODO: fix arguments
ppu_validate(lle_dir + name, prx->funcs, prx->funcs[0].addr);
prx->validate(prx->funcs[0].addr);
}
loaded_modules.emplace_back(std::move(prx));
@ -1230,17 +1252,15 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
}
{
// Analyse executable (TODO)
ppu_module _main;
_main.funcs = ppu_analyse(segments, sections, 0, elf.header.e_entry);
// Set path (TODO)
_main->name = "";
_main->path = vfs::get(Emu.GetPath());
// Validate analyser results (not required)
ppu_validate(vfs::get(Emu.GetPath()), _main.funcs, 0);
// Analyse executable (TODO)
_main->analyse(0, static_cast<u32>(elf.header.e_entry));
// Set for delayed initialization in ppu_initialize()
fxm::make<ppu_module>(std::move(_main));
}
// Validate analyser results (not required)
_main->validate(0);
// Set SDK version
g_ps3_sdk_version = sdk_version;
@ -1262,7 +1282,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
*argv++ = arg_addr;
}
argv -= args.size();
argv -= ::size32(args);
// Initialize main thread
auto ppu = idm::make_ptr<ppu_thread>("main_thread", primary_prio, primary_stacksize);