HLE linkage rewritten

This commit is contained in:
Nekotekina 2017-04-13 02:31:42 +03:00
parent 14f02b23c2
commit 7428bb3025
8 changed files with 163 additions and 157 deletions

View file

@ -125,6 +125,8 @@ cfg::bool_entry g_cfg_load_libreq(cfg::root.core, "Load required libraries", tru
cfg::set_entry g_cfg_load_libs(cfg::root.core, "Load libraries");
extern cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder;
extern std::string ppu_get_function_name(const std::string& module, u32 fnid);
extern std::string ppu_get_variable_name(const std::string& module, u32 vnid);
extern void ppu_register_range(u32 addr, u32 size);
@ -136,6 +138,9 @@ extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
extern u32 g_ps3_sdk_version;
// HLE function name cache
std::vector<std::string> g_ppu_function_names;
extern u32 ppu_generate_id(const char* name)
{
// Symbol name suffix
@ -195,13 +200,16 @@ struct ppu_linkage_info
{
struct info
{
bool hle = false;
u32 export_addr = 0;
std::set<u32> imports;
};
// FNID -> (export; [imports...])
std::map<u32, info> functions;
std::map<u32, info> variables;
std::unordered_map<u32, info, value_hash<u32>> functions;
std::unordered_map<u32, info, value_hash<u32>> variables;
bool imported = false;
};
// Module map
@ -209,7 +217,7 @@ struct ppu_linkage_info
};
// Initialize static modules.
static void ppu_initialize_modules()
static void ppu_initialize_modules(const std::shared_ptr<ppu_linkage_info>& link)
{
const std::initializer_list<const ppu_static_module*> registered
{
@ -312,23 +320,6 @@ static void ppu_initialize_modules()
&ppu_module_manager::sys_lv2dbg,
};
// "Use" all the modules for correct linkage
for (auto& module : registered)
{
LOG_TRACE(LOADER, "Registered static module: %s", module->name);
for (auto& function : module->functions)
{
LOG_TRACE(LOADER, "** 0x%08X: %s", function.first, function.second.name);
}
for (auto& variable : module->variables)
{
LOG_TRACE(LOADER, "** &0x%08X: %s (size=0x%x, align=0x%x)", variable.first, variable.second.name, variable.second.size, variable.second.align);
variable.second.var->set(0);
}
}
// Initialize double-purpose fake OPD array for HLE functions
const auto& hle_funcs = ppu_function_manager::get();
@ -352,6 +343,86 @@ static void ppu_initialize_modules()
// Set memory protection to read-only
vm::page_protect(ppu_function_manager::addr, ::align(::size32(hle_funcs) * 8, 0x1000), 0, 0, vm::page_writable);
// Initialize function names
const bool is_first = g_ppu_function_names.empty();
if (is_first)
{
g_ppu_function_names.resize(hle_funcs.size());
g_ppu_function_names[0] = "INVALID";
g_ppu_function_names[1] = "HLE RETURN";
}
// For HLE variable allocation
u32 alloc_addr = 0;
// "Use" all the modules for correct linkage
for (auto& module : registered)
{
LOG_TRACE(LOADER, "Registered static module: %s", module->name);
auto& linkage = link->modules[module->name];
for (auto& function : module->functions)
{
LOG_TRACE(LOADER, "** 0x%08X: %s", function.first, function.second.name);
if (is_first)
{
g_ppu_function_names[function.second.index] = fmt::format("%s.%s", module->name, function.second.name);
}
if ((function.second.flags & MFF_HIDDEN) == 0)
{
auto& flink = linkage.functions[function.first];
flink.hle = true;
flink.export_addr = ppu_function_manager::addr + 8 * function.second.index;
}
}
for (auto& variable : module->variables)
{
LOG_TRACE(LOADER, "** &0x%08X: %s (size=0x%x, align=0x%x)", variable.first, variable.second.name, variable.second.size, variable.second.align);
// Allocate HLE variable
if (variable.second.size >= 4096 || variable.second.align >= 4096)
{
variable.second.var->set(vm::alloc(variable.second.size, vm::main, std::max<u32>(variable.second.align, 4096)));
}
else
{
const u32 next = ::align(alloc_addr, variable.second.align);
const u32 end = next + variable.second.size;
if (!next || (end >> 12 != alloc_addr >> 12))
{
alloc_addr = vm::alloc(4096, vm::main);
}
else
{
alloc_addr = next;
}
variable.second.var->set(alloc_addr);
alloc_addr += variable.second.size;
}
LOG_TRACE(LOADER, "Allocated HLE variable %s.%s at 0x%x", module->name, variable.second.name, variable.second.var->addr());
// Initialize HLE variable
if (variable.second.init)
{
variable.second.init();
}
auto& vlink = linkage.variables[variable.first];
vlink.hle = true;
vlink.export_addr = variable.second.var->addr();
}
}
}
// Link variable
@ -452,6 +523,9 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
// Static module
const auto _sm = ppu_module_manager::get_module(module_name);
// Module linkage
auto& mlink = link->modules[module_name];
const auto fnids = +lib.nids;
const auto faddrs = +lib.addrs;
@ -463,13 +537,13 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
LOG_NOTICE(LOADER, "**** %s export: [%s] at 0x%x", module_name, ppu_get_function_name(module_name, fnid), faddr);
// Function linkage info
auto& flink = link->modules[module_name].functions[fnid];
auto& flink = mlink.functions[fnid];
if (flink.export_addr)
if (flink.export_addr && !flink.hle)
{
LOG_FATAL(LOADER, "Already linked function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
LOG_ERROR(LOADER, "Already linked function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name);
}
else
//else
{
// Static function
const auto _sf = _sm && _sm->functions.count(fnid) ? &_sm->functions.at(fnid) : nullptr;
@ -499,6 +573,7 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
{
// Set exported function
flink.export_addr = faddr;
flink.hle = false;
// Fix imports
for (const u32 addr : flink.imports)
@ -521,16 +596,17 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
LOG_NOTICE(LOADER, "**** %s export: &[%s] at 0x%x", module_name, ppu_get_variable_name(module_name, vnid), vaddr);
// Variable linkage info
auto& vlink = link->modules[module_name].variables[vnid];
auto& vlink = mlink.variables[vnid];
if (vlink.export_addr)
if (vlink.export_addr && !vlink.hle)
{
LOG_FATAL(LOADER, "Already linked variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
LOG_ERROR(LOADER, "Already linked variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name);
}
else
//else
{
// Set exported variable
vlink.export_addr = vaddr;
vlink.hle = false;
// Fix imports
for (const auto vref : vlink.imports)
@ -565,6 +641,9 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
// Static module
const auto _sm = ppu_module_manager::get_module(module_name);
// Module linkage
auto& mlink = link->modules[module_name];
const auto fnids = +lib.nids;
const auto faddrs = +lib.addrs;
@ -580,12 +659,17 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
// Add new import
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);
}
//LOG_WARNING(LOADER, "Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr);
}
@ -604,6 +688,7 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
// Add new import
vlink.imports.emplace(vref);
mlink.imported = true;
// Link if available
if (vlink.export_addr)
@ -620,6 +705,12 @@ 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)
{
if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm && name == "libfiber.sprx")
{
LOG_FATAL(PPU, "libfiber.sprx is not compatible with PPU LLVM Recompiler. Use PPU Interpreter.");
Emu.Pause();
}
std::vector<std::pair<u32, u32>> segments;
std::vector<std::pair<u32, u32>> sections;
@ -889,7 +980,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
// Initialize HLE modules
ppu_initialize_modules();
ppu_initialize_modules(link);
// Load other programs
for (auto& prog : elf.progs)
@ -1120,8 +1211,14 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
}
// TODO: recursively scan all SPRX files in /app_home/ for imports
for (const auto& pair : link->modules)
{
if (!pair.second.imported)
{
continue;
}
for (auto range = sprx_map.equal_range(pair.first); range.first != range.second; ++range.first)
{
load_libs.emplace(range.first->second);
@ -1169,106 +1266,6 @@ void ppu_load_exec(const ppu_exec_object& elf)
}
}
// Check unlinked functions and variables
for (auto& module : link->modules)
{
const auto _sm = ppu_module_manager::get_module(module.first);
if (!_sm)
{
LOG_ERROR(LOADER, "Unknown module '%s'", module.first);
}
else
{
// Allocate HLE variables (TODO)
for (auto& var : _sm->variables)
{
var.second.var->set(vm::alloc(var.second.size, vm::main, std::max<u32>(var.second.align, 4096)));
LOG_WARNING(LOADER, "Allocated variable '%s' in module '%s' at *0x%x", var.second.name, module.first, var.second.var->addr());
}
// Initialize HLE variables (TODO)
for (auto& var : _sm->variables)
{
var.second.init();
}
}
for (auto& entry : module.second.functions)
{
const u32 fnid = entry.first;
const u32 faddr = entry.second.export_addr;
if (faddr == 0)
{
const std::string fname = ppu_get_function_name(module.first, fnid);
// Link HLE implementation if available
if (const auto _sf = _sm && _sm->functions.count(fnid) ? &_sm->functions.at(fnid) : nullptr)
{
LOG_NOTICE(LOADER, "Linking HLE function '%s' in module '%s' (index %u)", fname, module.first, _sf->index);
for (const u32 import : entry.second.imports)
{
LOG_TRACE(LOADER, "** Linked at *0x%x (0x%x)", import, vm::read32(import));
vm::write32(import, ppu_function_manager::addr + 8 * _sf->index);
}
}
else
{
LOG_ERROR(LOADER, "Unknown function '%s' in module '%s'", fname, module.first);
for (const u32 import : entry.second.imports)
{
LOG_WARNING(LOADER, "** Not linked at *0x%x (0x%x)", import, vm::read32(import));
vm::write32(import, ppu_function_manager::addr);
}
}
}
}
for (auto& entry : module.second.variables)
{
const u32 vnid = entry.first;
const u32 vaddr = entry.second.export_addr;
if (vaddr == 0)
{
const std::string vname = ppu_get_variable_name(module.first, vnid);
// Link HLE variable if available
if (const auto _sv = _sm && _sm->variables.count(vnid) ? &_sm->variables.at(vnid) : nullptr)
{
LOG_NOTICE(LOADER, "Linking HLE variable '%s' in module '%s' (*0x%x):", vname, module.first, _sv->var->addr());
for (const u32 ref : entry.second.imports)
{
ppu_patch_variable_refs(ref, _sv->var->addr());
LOG_NOTICE(LOADER, "** Linked at ref=*0x%x", ref);
}
}
else
{
LOG_ERROR(LOADER, "Unknown variable '%s' in module '%s'", vname, module.first);
for (const u32 ref : entry.second.imports)
{
LOG_WARNING(LOADER, "** Not linked at ref=*0x%x", ref);
}
}
}
else
{
// Retro-link LLE variable (TODO: HLE must not be allocated/initialized in this case)
if (const auto _sv = _sm && _sm->variables.count(vnid) ? &_sm->variables.at(vnid) : nullptr)
{
_sv->var->set(vaddr);
LOG_NOTICE(LOADER, "Linked LLE variable '%s' in module '%s' -> 0x%x", ppu_get_variable_name(module.first, vnid), module.first, vaddr);
}
}
}
}
{
// Analyse executable
std::vector<ppu_function> main_funcs = ppu_analyse(segments, sections, 0, elf.header.e_entry);
@ -1307,7 +1304,9 @@ void ppu_load_exec(const ppu_exec_object& elf)
ppu->cmd_push({ppu_cmd::initialize, 0});
// TODO: adjust for liblv2 loading option
if (!g_cfg_load_liblv2)
u32 entry = static_cast<u32>(elf.header.e_entry);
if (!g_cfg_load_liblv2 || g_cfg_load_libreq)
{
// Set TLS args, call sys_initialize_tls
ppu->cmd_list
@ -1316,6 +1315,13 @@ void ppu_load_exec(const ppu_exec_object& elf)
{ ppu_cmd::hle_call, FIND_FUNC(sys_initialize_tls) },
});
}
else
{
// Run liblv2.sprx entry point (TODO)
entry = loaded_modules[0]->start.addr();
loaded_modules.clear();
}
// Run start functions
for (const auto& prx : loaded_modules)
@ -1337,9 +1343,9 @@ void ppu_load_exec(const ppu_exec_object& elf)
ppu->cmd_list
({
{ ppu_cmd::set_args, 8 }, u64{args.size()}, u64{argv.addr()}, u64{envp.addr()}, u64{0}, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize},
{ ppu_cmd::set_gpr, 11 }, u64{0xabadcafe},
{ ppu_cmd::set_gpr, 11 }, u64{elf.header.e_entry},
{ ppu_cmd::set_gpr, 12 }, u64{malloc_pagesize},
{ ppu_cmd::lle_call, static_cast<u32>(elf.header.e_entry) },
{ ppu_cmd::lle_call, entry },
});
// Set actual memory protection (experimental)